Merge branch 'master' into engine

This commit is contained in:
Adrian Gallagher
2019-08-23 21:59:01 +10:00
parent 94a784d63f
commit 2dc813b5f3
97 changed files with 111512 additions and 1223 deletions

View File

@@ -30,7 +30,12 @@ test_script:
# test back-end
- go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.16.0
- '%GOPATH%\bin\golangci-lint.exe run --verbose'
- go test -race ./...
- ps: >-
if($env:APPVEYOR_SCHEDULED_BUILD -eq 'true') {
go test -race ./... -tags=mock_test_off
}else {
go test -race ./...
}
# test front-end
- node --version

View File

@@ -25,9 +25,13 @@ CodeLingoBot | https://github.com/CodeLingoBot
CodeLingoTeam | https://github.com/CodeLingoTeam
Daanikus | https://github.com/Daanikus
daniel-cohen | https://github.com/daniel-cohen
DirectX | https://github.com/DirectX
frankzougc | https://github.com/frankzougc
starit | https://github.com/starit
Jimexist | https://github.com/Jimexist
lookfirst | https://github.com/lookfirst
mattkanwisher | https://github.com/mattkanwisher
mKurrels | https://github.com/mKurrels
m1kola | https://github.com/m1kola
cavapoo2 | https://github.com/cavapoo2
zeldrinn | https://github.com/zeldrinn

View File

@@ -4,6 +4,7 @@ LINTPKG = github.com/golangci/golangci-lint/cmd/golangci-lint@v1.16.0
LINTBIN = $(GOPATH)/bin/golangci-lint
GCTLISTENPORT=9050
GCTPROFILERLISTENPORT=8085
CRON = $(TRAVIS_EVENT_TYPE)
get:
GO111MODULE=on go get $(GCTPKG)
@@ -16,7 +17,11 @@ linter:
check: linter test
test:
ifeq ($(CRON), cron)
go test -race -tags=mock_test_off -coverprofile=coverage.txt -covermode=atomic ./...
else
go test -race -coverprofile=coverage.txt -covermode=atomic ./...
endif
build:
GO111MODULE=on go build $(LDFLAGS)

View File

@@ -39,7 +39,8 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Huobi.Hadax | Yes | Yes | NA |
| ItBit | Yes | NA | No |
| Kraken | Yes | Yes | NA |
| LakeBTC | Yes | Yes | NA |
| Lbank | Yes | No | NA |
| LakeBTC | Yes | No | NA |
| LocalBitcoins | Yes | NA | NA |
| OKCoin International | Yes | Yes | No |
| OKEX | Yes | Yes | No |
@@ -129,10 +130,11 @@ Binaries will be published once the codebase reaches a stable condition.
### A very special thank you to all who have contributed to this program:
|User|Github|Contribution Amount|
|--|--|--|| thrasher- | https://github.com/thrasher- | 540 |
| shazbert | https://github.com/shazbert | 173 |
| gloriousCode | https://github.com/gloriousCode | 150 |
| xtda | https://github.com/xtda | 17 |
|--|--|--|
| thrasher- | https://github.com/thrasher- | 543 |
| shazbert | https://github.com/shazbert | 174 |
| gloriousCode | https://github.com/gloriousCode | 154 |
| xtda | https://github.com/xtda | 18 |
| ermalguni | https://github.com/ermalguni | 14 |
| vadimzhukck | https://github.com/vadimzhukck | 10 |
| 140am | https://github.com/140am | 8 |
@@ -154,10 +156,13 @@ Binaries will be published once the codebase reaches a stable condition.
| CodeLingoTeam | https://github.com/CodeLingoTeam | 1 |
| Daanikus | https://github.com/Daanikus | 1 |
| daniel-cohen | https://github.com/daniel-cohen | 1 |
| DirectX | https://github.com/DirectX | 1 |
| frankzougc | https://github.com/frankzougc | 1 |
| starit | https://github.com/starit | 1 |
| Jimexist | https://github.com/Jimexist | 1 |
| lookfirst | https://github.com/lookfirst | 1 |
| mattkanwisher | https://github.com/mattkanwisher | 1 |
| mKurrels | https://github.com/mKurrels | 1 |
| m1kola | https://github.com/m1kola | 1 |
| cavapoo2 | https://github.com/cavapoo2 | 1 |
| zeldrinn | https://github.com/zeldrinn | 1 |

View File

@@ -0,0 +1,98 @@
{{define "exchanges lbank" -}}
{{template "header" .}}
## Lbank Exchange
### Current Features
+ REST Support
### How to enable
+ [Enable via configuration](https://githul.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### How to do REST public/private calls
+ If enabled via "configuration".json file the exchange will be added to the
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
the wrapper interface functions for accessing exchange data. View routines.go
for an example of integration usage with GoCryptoTrader. Rudimentary example
below:
main.go
```go
var l exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "Lbank" {
l = bot.exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := l.GetTickerPrice()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := l.GetOrderbookEx()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := l.GetAccountInfo()
if err != nil {
// Handle error
}
```
+ If enabled via individually importing package, rudimentary example below:
```go
// Public calls
// Fetches current ticker information
ticker, err := l.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := l.GetOrderBook()
if err != nil {
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetUserInfo returns account info
accountInfo, err := l.GetUserInfo(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := l.Trade(...)
if err != nil {
// Handle error
}
```
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
{{template "donations"}}
{{end}}

View File

@@ -40,7 +40,8 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
| Huobi.Hadax | Yes | Yes | NA |
| ItBit | Yes | NA | No |
| Kraken | Yes | Yes | NA |
| LakeBTC | Yes | Yes | NA |
| Lbank | Yes | No | NA |
| LakeBTC | Yes | No | NA |
| LocalBitcoins | Yes | NA | NA |
| OKCoin International | Yes | Yes | No |
| OKEX | Yes | Yes | No |

View File

@@ -85,7 +85,6 @@ have multiple deposit accounts for different FIAT deposit currencies.
"websocketResponseCheckTimeout": 30000000,
"websocketResponseMaxLimit": 7000000000,
"httpTimeout": 15000000000,
"AuthenticatedAPISupport": false,
"APIKey": "Key",
"APISecret": "Secret",
"AvailablePairs": "ATENC_GBP,ATENC_NZD,BTC_AUD,BTC_SGD,LTC_BTC,START_GBP,...",

View File

@@ -14,7 +14,7 @@ import (
const (
// Default number of enabled exchanges. Modify this whenever an exchange is
// added or removed
defaultEnabledExchanges = 27
defaultEnabledExchanges = 28
)
func TestGetCurrencyConfig(t *testing.T) {
@@ -465,7 +465,7 @@ func TestCountEnabledExchanges(t *testing.T) {
}
enabledExch := GetConfigEnabledExchanges.CountEnabledExchanges()
if enabledExch != defaultEnabledExchanges {
t.Error("Test failed. GetConfigEnabledExchanges is wrong")
t.Errorf("Test failed. Expected %v, Received %v", defaultEnabledExchanges, enabledExch)
}
}

View File

@@ -1107,6 +1107,48 @@
}
]
},
{
"name": "LBank",
"enabled": true,
"verbose": false,
"websocket": false,
"useSandbox": false,
"restPollingDelay": 10,
"httpTimeout": 15000000000,
"httpUserAgent": "",
"httpDebugging": false,
"authenticatedApiSupport": false,
"apiKey": "Key",
"apiSecret": "Secret",
"apiUrl": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"apiUrlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"proxyAddress": "",
"websocketUrl": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API",
"availablePairs": "fbc_usdt,hds_usdt,galt_usdt,dxn_usdt,iog_usdt,ioex_usdt,vollar_usdt,oath_usdt,bloc_usdt,btc_lbcn,eth_lbcn,usdt_lbcn,btc_usdt,eth_usdt,eth_btc,abbc_btc,bzky_eth,onot_eth,kisc_eth,bxa_usdt,atp_usdt,mat_usdt,sky_btc,sky_lbcn,rnt_usdt,vena_usdt,grin_usdt,ida_usdt,pnt_usdt,bsv_btc,bsv_usdt,opx_usdt,tena_eth,seer_lbcn,vet_lbcn,vtho_btc,vnx_lbcn,vnx_btc,amo_eth,ubex_btc,eos_btc,ubex_usdt,tns_lbcn,tns_btc,ali_eth,sdc_eth,sait_eth,artcn_usdt,dax_btc,dax_eth,dali_usdt,vet_usdt,ten_usdt,bch_usdt,neo_usdt,qtum_usdt,zec_usdt,vet_btc,pai_btc,pnt_btc,bch_btc,ltc_btc,neo_btc,dash_btc,etc_btc,qtum_btc,zec_btc,sc_btc,bts_btc,cpx_btc,xwc_btc,fil6_btc,fil12_btc,fil36_btc,eos_usdt,ut_eth,ela_eth,vet_eth,vtho_eth,pai_eth,bfdt_eth,her_eth,ptt_eth,tac_eth,idhub_eth,ssc_eth,skm_eth,iic_eth,ply_eth,ext_eth,eos_eth,yoyow_eth,trx_eth,qtum_eth,zec_eth,bts_eth,btm_eth,mith_eth,nas_eth,man_eth,dbc_eth,bto_eth,ddd_eth,cpx_eth,cs_eth,iht_eth,tky_eth,ocn_eth,dct_eth,zpt_eth,eko_eth,mda_eth,pst_eth,xwc_eth,put_eth,pnt_eth,aac_eth,fil6_eth,fil12_eth,fil36_eth,uip_eth,seer_eth,bsb_eth,cdc_eth,grams_eth,ddmx_eth,eai_eth,inc_eth,bnb_usdt,ht_usdt,bot_eth,kbc_btc,kbc_usdt,mai_usdt,phv_usdt,hnb_usdt,gt_usdt,b91_usdt,voken_usdt,cye_usdt,brc_usdt,btc_ausd",
"enabledPairs": "btc_usdt",
"baseCurrencies": "USD",
"assetTypes": "SPOT",
"supportsAutoPairUpdates": true,
"configCurrencyPairFormat": {
"uppercase": false,
"delimiter": "_"
},
"requestCurrencyPairFormat": {
"uppercase": false,
"delimiter": "_"
},
"bankAccounts": [
{
"bankName": "",
"bankAddress": "",
"accountName": "",
"accountNumber": "",
"swiftCode": "",
"iban": "",
"supportedCurrencies": ""
}
]
},
{
"name": "LocalBitcoins",
"enabled": true,

View File

@@ -733,6 +733,7 @@ func (c *Coinmarketcap) SendHTTPRequest(method, endpoint string, v url.Values, r
false,
false,
c.Verbose,
false,
false)
}

View File

@@ -193,6 +193,7 @@ func (c *CurrencyConverter) SendHTTPRequest(endPoint string, values url.Values,
auth,
false,
c.Verbose,
false,
false)
if err != nil {
return fmt.Errorf("currency converter API SendHTTPRequest error %s with path %s",

View File

@@ -243,5 +243,6 @@ func (c *CurrencyLayer) SendHTTPRequest(endPoint string, values url.Values, resu
auth,
false,
c.Verbose,
false,
false)
}

View File

@@ -179,6 +179,7 @@ func (e *ExchangeRates) SendHTTPRequest(endPoint string, values url.Values, resu
false,
false,
e.Verbose,
false,
false)
if err != nil {
return fmt.Errorf("exchangeRatesAPI SendHTTPRequest error %s with path %s",

View File

@@ -267,5 +267,6 @@ func (f *Fixer) SendOpenHTTPRequest(endpoint string, v url.Values, result interf
auth,
false,
f.Verbose,
false,
false)
}

View File

@@ -267,5 +267,6 @@ func (o *OXR) SendHTTPRequest(endpoint string, values url.Values, result interfa
false,
false,
o.Verbose,
false,
false)
}

View File

@@ -29,6 +29,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/itbit"
"github.com/thrasher-corp/gocryptotrader/exchanges/kraken"
"github.com/thrasher-corp/gocryptotrader/exchanges/lakebtc"
"github.com/thrasher-corp/gocryptotrader/exchanges/lbank"
"github.com/thrasher-corp/gocryptotrader/exchanges/localbitcoins"
"github.com/thrasher-corp/gocryptotrader/exchanges/okcoin"
"github.com/thrasher-corp/gocryptotrader/exchanges/okex"
@@ -174,6 +175,8 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error {
exch = new(kraken.Kraken)
case "lakebtc":
exch = new(lakebtc.LakeBTC)
case "lbank":
exch = new(lbank.Lbank)
case "localbitcoins":
exch = new(localbitcoins.LocalBitcoins)
case "okcoin international":

View File

@@ -517,7 +517,16 @@ func (a *Alphapoint) SendHTTPRequest(method, path string, data map[string]interf
return errors.New("unable to JSON request")
}
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, false, false, a.Verbose, a.HTTPDebugging)
return a.SendPayload(method,
path,
headers,
bytes.NewBuffer(PayloadJSON),
result,
false,
false,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated request
@@ -543,5 +552,14 @@ func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[
return errors.New("unable to JSON request")
}
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, true, true, a.Verbose, a.HTTPDebugging)
return a.SendPayload(method,
path,
headers,
bytes.NewBuffer(PayloadJSON),
result,
true,
true,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}

View File

@@ -176,9 +176,13 @@ func (a *ANX) NewOrder(orderType string, buy bool, tradedCurrency string, traded
// CancelOrderByIDs cancels orders, requires already knowing order IDs
// There is no existing API call to retrieve orderIds
func (a *ANX) CancelOrderByIDs(orderIds []string) (OrderCancelResponse, error) {
var response OrderCancelResponse
if len(orderIds) == 0 {
return response, errors.New("no order ids provided")
}
req := make(map[string]interface{})
req["orderIds"] = orderIds
var response OrderCancelResponse
err := a.SendAuthenticatedHTTPRequest(anxOrderCancel, req, &response)
if response.ResultCode != "OK" {
@@ -194,7 +198,7 @@ func (a *ANX) GetOrderList(isActiveOrdersOnly bool) ([]OrderResponse, error) {
req["activeOnly"] = isActiveOrdersOnly
type OrderListResponse struct {
Timestamp int64 `json:"timestamp"`
Timestamp int64 `json:"timestamp,string"`
ResultCode string `json:"resultCode"`
Count int64 `json:"count"`
OrderResponses []OrderResponse `json:"orders"`
@@ -206,7 +210,6 @@ func (a *ANX) GetOrderList(isActiveOrdersOnly bool) ([]OrderResponse, error) {
}
if response.ResultCode != "OK" {
log.Errorf(log.ExchangeSys, "Response code is not OK: %s\n", response.ResultCode)
return nil, errors.New(response.ResultCode)
}
@@ -232,7 +235,6 @@ func (a *ANX) OrderInfo(orderID string) (OrderResponse, error) {
}
if response.ResultCode != "OK" {
log.Errorf(log.ExchangeSys, "Response code is not OK: %s\n", response.ResultCode)
return OrderResponse{}, errors.New(response.ResultCode)
}
return response.Order, nil
@@ -252,7 +254,7 @@ func (a *ANX) Send(currency, address, otp, amount string) (string, error) {
type SendResponse struct {
TransactionID string `json:"transactionId"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
Timestamp int64 `json:"timestamp,string"`
}
var response SendResponse
@@ -263,7 +265,6 @@ func (a *ANX) Send(currency, address, otp, amount string) (string, error) {
}
if response.ResultCode != "OK" {
log.Errorf(log.ExchangeSys, "Response code is not OK: %s\n", response.ResultCode)
return "", errors.New(response.ResultCode)
}
return response.TransactionID, nil
@@ -289,7 +290,6 @@ func (a *ANX) CreateNewSubAccount(currency, name string) (string, error) {
}
if response.ResultCode != "OK" {
log.Errorf(log.ExchangeSys, "Response code is not OK: %s\n", response.ResultCode)
return "", errors.New(response.ResultCode)
}
return response.SubAccount, nil
@@ -308,7 +308,7 @@ func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (
Address string `json:"address"`
SubAccount string `json:"subAccount"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
Timestamp int64 `json:"timestamp,string"`
}
var response AddressResponse
@@ -323,7 +323,6 @@ func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (
}
if response.ResultCode != "OK" {
log.Errorf(log.ExchangeSys, "Response code is not OK: %s\n", response.ResultCode)
return "", errors.New(response.ResultCode)
}
@@ -332,7 +331,16 @@ func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (
// SendHTTPRequest sends an unauthenticated HTTP request
func (a *ANX) SendHTTPRequest(path string, result interface{}) error {
return a.SendPayload(http.MethodGet, path, nil, nil, result, false, false, a.Verbose, a.HTTPDebugging)
return a.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends a authenticated HTTP request
@@ -365,9 +373,16 @@ func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interf
headers["Rest-Sign"] = crypto.Base64Encode(hmac)
headers["Content-Type"] = "application/json"
return a.SendPayload(http.MethodPost, a.API.Endpoints.URL+path, headers,
bytes.NewBuffer(PayloadJSON), result, true, true, a.Verbose,
a.HTTPDebugging)
return a.SendPayload(http.MethodPost,
a.API.Endpoints.URL+path,
headers,
bytes.NewBuffer(PayloadJSON),
result,
true,
true,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -0,0 +1,32 @@
//+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 anx
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()
cfg.LoadConfig("../../testdata/configtest.json")
anxConfig, err := cfg.GetExchangeConfig("ANX")
if err != nil {
log.Fatalf("Test Failed - ANX Setup() init error: %s", err)
}
anxConfig.API.AuthenticatedSupport = true
anxConfig.API.Credentials.Key = apiKey
anxConfig.API.Credentials.Secret = apiSecret
a.SetDefaults()
a.Setup(anxConfig)
log.Printf(sharedtestvalues.LiveTesting, a.GetName(), a.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,45 @@
//+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 anx
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/anx/anx.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
anxConfig, err := cfg.GetExchangeConfig("ANX")
if err != nil {
log.Fatal("Test Failed - Mock server error", err)
}
a.SkipAuthCheck = true
anxConfig.API.AuthenticatedSupport = true
anxConfig.API.Credentials.Key = apiKey
anxConfig.API.Credentials.Secret = apiSecret
a.SetDefaults()
a.Setup(anxConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockFile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
a.HTTPClient = newClient
a.API.Endpoints.URL = serverDetails + "/"
log.Printf(sharedtestvalues.MockTesting, a.GetName(), a.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -4,7 +4,6 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
@@ -19,52 +18,16 @@ const (
var a ANX
func TestSetDefaults(t *testing.T) {
a.SetDefaults()
if a.Name != "ANX" {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if !a.Enabled {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if !a.Verbose {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
}
func TestSetup(t *testing.T) {
anxSetupConfig := config.GetConfig()
anxSetupConfig.LoadConfig("../../testdata/configtest.json")
anxConfig, err := anxSetupConfig.GetExchangeConfig("ANX")
if err != nil {
t.Error("Test Failed - ANX Setup() init error")
}
a.Setup(anxConfig)
a.API.Credentials.Key = apiKey
a.API.Credentials.Secret = apiSecret
a.API.AuthenticatedSupport = true
if !a.Enabled {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if a.Verbose {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if len(a.BaseCurrencies) == 0 {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
}
func TestGetCurrencies(t *testing.T) {
t.Parallel()
_, err := a.GetCurrencies()
if err != nil {
t.Fatalf("Test failed. TestGetCurrencies failed. Err: %s", err)
}
}
func TestFetchTradablePairs(t *testing.T) {
func TestGetTradablePairs(t *testing.T) {
t.Parallel()
_, err := a.FetchTradablePairs(asset.Spot)
if err != nil {
t.Fatalf("Test failed. TestGetTradablePairs failed. Err: %s", err)
@@ -72,6 +35,7 @@ func TestFetchTradablePairs(t *testing.T) {
}
func TestGetTicker(t *testing.T) {
t.Parallel()
ticker, err := a.GetTicker("BTCUSD")
if err != nil {
t.Errorf("Test Failed - ANX GetTicker() error: %s", err)
@@ -82,16 +46,18 @@ func TestGetTicker(t *testing.T) {
}
func TestGetDepth(t *testing.T) {
ticker, err := a.GetDepth("BTCUSD")
t.Parallel()
depth, err := a.GetDepth("BTCUSD")
if err != nil {
t.Errorf("Test Failed - ANX GetDepth() error: %s", err)
}
if ticker.Result != "success" {
if depth.Result != "success" {
t.Error("Test Failed - ANX GetDepth() unsuccessful")
}
}
func TestGetAPIKey(t *testing.T) {
t.Parallel()
apiKey, apiSecret, err := a.GetAPIKey("userName", "passWord", "", "1337")
if err == nil {
t.Error("Test Failed - ANX GetAPIKey() Incorrect")
@@ -116,6 +82,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
a.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
@@ -130,9 +97,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
a.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
@@ -202,7 +167,7 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
a.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " +
exchange.WithdrawCryptoWithEmailText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
@@ -214,34 +179,36 @@ func TestFormatWithdrawPermissions(t *testing.T) {
}
func TestGetActiveOrders(t *testing.T) {
a.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := a.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
a.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := a.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetBalance() error", err)
}
}
@@ -253,10 +220,8 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -267,13 +232,14 @@ func TestSubmitOrder(t *testing.T) {
Quote: currency.USD,
},
OrderSide: exchange.BuyOrderSide,
OrderType: exchange.LimitOrderType,
OrderType: exchange.MarketOrderType,
Price: 1,
Amount: 1,
ClientID: "meowOrder",
}
response, err := a.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests {
// TODO: QA Pass to submit order
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
@@ -281,10 +247,8 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -298,18 +262,19 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := a.CancelOrder(orderCancellation)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel order: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not cancel order: %s", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -323,11 +288,13 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := a.CancelAllOrders(orderCancellation)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel order: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Errorf("QA pass needs to be completed and mock needs to be updated error cannot be nil")
}
if len(resp.OrderStatus) > 0 {
@@ -336,20 +303,20 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestGetAccountInfo(t *testing.T) {
if apiKey != "" || apiSecret != "" {
_, err := a.GetAccountInfo()
if err != nil {
t.Error("test failed - GetAccountInfo() error:", err)
}
} else {
_, err := a.GetAccountInfo()
if err == nil {
t.Error("test failed - GetAccountInfo() error")
}
t.Parallel()
_, err := a.GetAccountInfo()
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("test failed - GetAccountInfo() error:", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("test failed - GetAccountInfo() error")
case mockTests && err != nil:
t.Error("test failed - GetAccountInfo() error:", err)
}
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := a.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
@@ -357,10 +324,8 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -375,55 +340,54 @@ func TestWithdraw(t *testing.T) {
}
_, err := a.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if areTestAPIKeysSet() && err != nil {
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
} else if !areTestAPIKeysSet() && err == nil && mockTests {
t.Error("Expecting an error when no keys are set")
}
}
func TestWithdrawFiat(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := a.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := a.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestGetDepositAddress(t *testing.T) {
if areTestAPIKeysSet() {
_, err := a.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress() error", err)
}
} else {
_, err := a.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
t.Parallel()
_, err := a.GetDepositAddress(currency.BTC, "")
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Error("Test Failed - GetDepositAddress() error", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
}
func TestUpdateOrderbook(t *testing.T) {
t.Parallel()
q := currency.Pair{
Delimiter: "_",
Base: currency.BTC,
Quote: currency.USD}
_, err := a.UpdateOrderbook(q, "spot")
if err != nil {
t.Fatalf("Update for orderbook failed: %v", err)
}
}

View File

@@ -60,6 +60,7 @@ func (a *ANX) SetDefaults() {
}
a.API.CredentialsValidator.RequiresKey = true
a.API.CredentialsValidator.RequiresSecret = true
a.API.CredentialsValidator.RequiresBase64DecodeSecret = true
a.CurrencyPairs = currency.PairsManager{
AssetTypes: asset.Items{
@@ -464,7 +465,7 @@ func (a *ANX) GetWebsocket() (*wshandler.Websocket, error) {
// GetFeeByType returns an estimate of fee based on type of transaction
func (a *ANX) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !a.AllowAuthenticatedRequest() && // Todo check connection status
if (!a.AllowAuthenticatedRequest() || a.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}

View File

@@ -159,20 +159,10 @@ func (b *Binance) GetRecentTrades(rtr RecentTradeRequestParams) ([]RecentTrade,
// limit: Optional. Default 500; max 1000.
// fromID:
func (b *Binance) GetHistoricalTrades(symbol string, limit int, fromID int64) ([]HistoricalTrade, error) {
var resp []HistoricalTrade
if err := b.CheckLimit(limit); err != nil {
return resp, err
}
params := url.Values{}
params.Set("symbol", strings.ToUpper(symbol))
params.Set("limit", strconv.Itoa(limit))
params.Set("fromid", strconv.FormatInt(fromID, 10))
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, historicalTrades, params.Encode())
return resp, b.SendHTTPRequest(path, &resp)
// Dropping support due to response for market data is always
// {"code":-2014,"msg":"API-key format invalid."}
// TODO: replace with newer API vs REST endpoint
return nil, common.ErrFunctionNotSupported
}
// GetAggregatedTrades returns aggregated trade activity
@@ -476,7 +466,16 @@ func (b *Binance) GetAccount() (*Account, error) {
// SendHTTPRequest sends an unauthenticated request
func (b *Binance) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthHTTPRequest sends an authenticated HTTP request
@@ -512,7 +511,16 @@ func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, re
Message string `json:"msg"`
}{}
err := b.SendPayload(method, path, headers, bytes.NewBuffer(nil), &interim, true, false, b.Verbose, b.HTTPDebugging)
err := b.SendPayload(method,
path,
headers,
bytes.NewBuffer(nil),
&interim,
true,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
@@ -632,7 +640,7 @@ 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) (int64, error) {
func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string) (string, error) {
var resp WithdrawResponse
path := fmt.Sprintf("%s%s", b.API.Endpoints.URL, withdraw)
@@ -648,7 +656,7 @@ func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string
}
if err := b.SendAuthHTTPRequest(http.MethodPost, path, params, &resp); err != nil {
return -1, err
return "", err
}
if !resp.Success {

View File

@@ -0,0 +1,32 @@
//+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 binance
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()
cfg.LoadConfig("../../testdata/configtest.json")
binanceConfig, err := cfg.GetExchangeConfig("Binance")
if err != nil {
log.Fatal("Test Failed - Binance Setup() init error", err)
}
binanceConfig.API.AuthenticatedSupport = true
binanceConfig.API.Credentials.Key = apiKey
binanceConfig.API.Credentials.Secret = apiSecret
b.SetDefaults()
b.Setup(binanceConfig)
log.Printf(sharedtestvalues.LiveTesting, b.GetName(), b.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,45 @@
//+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 binance
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/binance/binance.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
binanceConfig, err := cfg.GetExchangeConfig("Binance")
if err != nil {
log.Fatal("Test Failed - Binance Setup() init error", err)
}
b.SkipAuthCheck = true
binanceConfig.API.AuthenticatedSupport = true
binanceConfig.API.Credentials.Key = apiKey
binanceConfig.API.Credentials.Secret = apiSecret
b.SetDefaults()
b.Setup(binanceConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
b.HTTPClient = newClient
b.API.Endpoints.URL = serverDetails
log.Printf(sharedtestvalues.MockTesting, b.GetName(), b.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -4,7 +4,6 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
@@ -19,26 +18,22 @@ const (
var b Binance
func TestSetDefaults(t *testing.T) {
b.SetDefaults()
func areTestAPIKeysSet() bool {
return b.ValidateAPICredentials()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
binanceConfig, err := cfg.GetExchangeConfig("Binance")
if err != nil {
t.Error("Test Failed - Binance Setup() init error")
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
FeeType: exchange.CryptocurrencyTradeFee,
Pair: currency.NewPair(currency.BTC, currency.LTC),
PurchasePrice: 1,
}
binanceConfig.API.AuthenticatedSupport = true
binanceConfig.API.Credentials.Key = apiKey
binanceConfig.API.Credentials.Secret = apiSecret
b.Setup(binanceConfig)
}
func TestFetchTradablePairs(t *testing.T) {
t.Parallel()
_, err := b.FetchTradablePairs(asset.Spot)
if err != nil {
t.Error("Test Failed - Binance FetchTradablePairs(asset asets.AssetType) error", err)
@@ -47,6 +42,7 @@ func TestFetchTradablePairs(t *testing.T) {
func TestGetOrderBook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderBook(OrderBookDataRequestParams{
Symbol: "BTCUSDT",
Limit: 10,
@@ -72,14 +68,19 @@ func TestGetRecentTrades(t *testing.T) {
func TestGetHistoricalTrades(t *testing.T) {
t.Parallel()
_, err := b.GetHistoricalTrades("BTCUSDT", 5, 1337)
if err == nil {
_, err := b.GetHistoricalTrades("BTCUSDT", 5, 0)
if !mockTests && err == nil {
t.Error("Test Failed - Binance GetHistoricalTrades() expecting error")
}
if mockTests && err == nil {
t.Error("Test Failed - Binance GetHistoricalTrades() error", err)
}
}
func TestGetAggregatedTrades(t *testing.T) {
t.Parallel()
_, err := b.GetAggregatedTrades("BTCUSDT", 5)
if err != nil {
t.Error("Test Failed - Binance GetAggregatedTrades() error", err)
@@ -88,6 +89,7 @@ func TestGetAggregatedTrades(t *testing.T) {
func TestGetSpotKline(t *testing.T) {
t.Parallel()
_, err := b.GetSpotKline(KlinesRequestParams{
Symbol: "BTCUSDT",
Interval: TimeIntervalFiveMinutes,
@@ -100,6 +102,7 @@ func TestGetSpotKline(t *testing.T) {
func TestGetAveragePrice(t *testing.T) {
t.Parallel()
_, err := b.GetAveragePrice("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetAveragePrice() error", err)
@@ -108,6 +111,7 @@ func TestGetAveragePrice(t *testing.T) {
func TestGetPriceChangeStats(t *testing.T) {
t.Parallel()
_, err := b.GetPriceChangeStats("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetPriceChangeStats() error", err)
@@ -116,6 +120,7 @@ func TestGetPriceChangeStats(t *testing.T) {
func TestGetTickers(t *testing.T) {
t.Parallel()
_, err := b.GetTickers()
if err != nil {
t.Error("Test Failed - Binance TestGetTickers error", err)
@@ -124,6 +129,7 @@ func TestGetTickers(t *testing.T) {
func TestGetLatestSpotPrice(t *testing.T) {
t.Parallel()
_, err := b.GetLatestSpotPrice("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetLatestSpotPrice() error", err)
@@ -132,117 +138,59 @@ func TestGetLatestSpotPrice(t *testing.T) {
func TestGetBestPrice(t *testing.T) {
t.Parallel()
_, err := b.GetBestPrice("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetBestPrice() error", err)
}
}
func TestNewOrder(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.NewOrder(&NewOrderRequest{
Symbol: "BTCUSDT",
Side: exchange.SellOrderSide.ToString(),
TradeType: BinanceRequestParamsOrderLimit,
TimeInForce: BinanceRequestParamsTimeGTC,
Quantity: 0.01,
Price: 1536.1,
})
if err == nil {
t.Error("Test Failed - Binance NewOrder() error", err)
}
}
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.CancelExistingOrder("BTCUSDT", 82584683, "")
if err != nil {
t.Error("Test Failed - Binance CancelExistingOrder() error", err)
}
}
func TestQueryOrder(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.QueryOrder("BTCUSDT", "", 1337)
if err == nil {
t.Error("Test Failed - Binance QueryOrder() error", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - QueryOrder() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - QueryOrder() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock QueryOrder() error", err)
}
}
func TestOpenOrders(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.OpenOrders("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance OpenOrders() error", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - OpenOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - OpenOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock OpenOrders() error", err)
}
}
func TestAllOrders(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.AllOrders("BTCUSDT", "", "")
if err != nil {
t.Error("Test Failed - Binance AllOrders() error", err)
}
}
func TestGetAccount(t *testing.T) {
if apiKey == "" || apiSecret == "" {
t.Skip()
}
t.Parallel()
b.SetDefaults()
TestSetup(t)
account, err := b.GetAccount()
if err != nil {
t.Fatal("Test Failed - Binance GetAccount() error", err)
}
if account.MakerCommission <= 0 {
t.Fatalf("Test Failed. Expected > 0, Received %d", account.MakerCommission)
}
if account.TakerCommission <= 0 {
t.Fatalf("Test Failed. Expected > 0, Received %d", account.TakerCommission)
}
t.Logf("Current makerFee: %d", account.MakerCommission)
t.Logf("Current takerFee: %d", account.TakerCommission)
}
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
FeeType: exchange.CryptocurrencyTradeFee,
Pair: currency.NewPair(currency.BTC, currency.LTC),
PurchasePrice: 1,
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - AllOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - AllOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock AllOrders() error", err)
}
}
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
@@ -257,12 +205,11 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() || mockTests {
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0.1) || err != nil {
t.Error(err)
@@ -332,19 +279,18 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := b.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
@@ -359,16 +305,18 @@ func TestGetActiveOrders(t *testing.T) {
}
_, err = b.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")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetActiveOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetActiveOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetActiveOrders() error", err)
}
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
@@ -384,125 +332,133 @@ func TestGetOrderHistory(t *testing.T) {
currency.BTC)}
_, err = b.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetOrderHistory() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetOrderHistory() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetOrderHistory() error", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// -----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
return b.ValidateAPICredentials()
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderSubmission = &exchange.OrderSubmission{
Pair: currency.Pair{
Delimiter: "_",
Base: currency.BTC,
Quote: currency.USD,
Base: currency.LTC,
Quote: currency.BTC,
},
OrderSide: exchange.BuyOrderSide,
OrderType: exchange.LimitOrderType,
OrderType: exchange.MarketOrderType,
Price: 1,
Amount: 1,
Amount: 1000000000,
ClientID: "meowOrder",
}
response, err := b.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
_, err := b.SubmitOrder(orderSubmission)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - SubmitOrder() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - SubmitOrder() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock SubmitOrder() error", err)
}
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
err := b.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - CancelExchangeOrder() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - CancelExchangeOrder() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelExchangeOrder() error", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
resp, err := b.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel order: %v", err)
}
if len(resp.OrderStatus) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.OrderStatus))
_, err := b.CancelAllOrders(orderCancellation)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - CancelAllExchangeOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - CancelAllExchangeOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelAllExchangeOrders() error", err)
}
}
func TestGetAccountInfo(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if apiKey == "" || apiSecret == "" {
t.Skip()
}
t.Parallel()
_, err := b.GetAccountInfo()
if err != nil {
t.Error("test failed - GetAccountInfo() error", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetAccountInfo() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetAccountInfo() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetAccountInfo() error", err)
}
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := b.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
t.Error("Test failed - ModifyOrder() error cannot be nil")
}
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
Amount: 0,
Currency: currency.BTC,
Description: "WITHDRAW IT ALL",
},
@@ -510,24 +466,20 @@ func TestWithdraw(t *testing.T) {
}
_, err := b.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Withdraw failed to be placed: %v", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - Withdraw() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - Withdraw() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock Withdraw() error", err)
}
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
t.Parallel()
var withdrawFiatRequest exchange.FiatWithdrawRequest
_, err := b.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
@@ -535,15 +487,9 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
t.Parallel()
var withdrawFiatRequest exchange.FiatWithdrawRequest
_, err := b.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
@@ -551,15 +497,15 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
if areTestAPIKeysSet() {
_, err := b.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress() error", err)
}
} else {
_, err := b.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
t.Parallel()
_, err := b.GetDepositAddress(currency.BTC, "")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetDepositAddress() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetDepositAddress() error", err)
}
}

View File

@@ -314,7 +314,7 @@ type NewOrderResponse struct {
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
Commission float64 `json:"commission,string"`
CommissionAsset float64 `json:"commissionAsset,string"`
CommissionAsset string `json:"commissionAsset"`
} `json:"fills"`
}
@@ -610,5 +610,5 @@ var WithdrawalFees = map[currency.Code]float64{
type WithdrawResponse struct {
Success bool `json:"success"`
Msg string `json:"msg"`
ID int64 `json:"id"`
ID string `json:"id"`
}

View File

@@ -444,9 +444,10 @@ func (b *Binance) GetDepositAddress(cryptocurrency currency.Code, _ string) (str
// submitted
func (b *Binance) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) {
amountStr := strconv.FormatFloat(withdrawRequest.Amount, 'f', -1, 64)
id, err := b.WithdrawCrypto(withdrawRequest.Currency.String(), withdrawRequest.Address, withdrawRequest.AddressTag, withdrawRequest.Description, amountStr)
return strconv.FormatInt(id, 10), err
return b.WithdrawCrypto(withdrawRequest.Currency.String(),
withdrawRequest.Address,
withdrawRequest.AddressTag,
withdrawRequest.Description, amountStr)
}
// WithdrawFiatFunds returns a withdrawal ID when a
@@ -468,7 +469,7 @@ func (b *Binance) GetWebsocket() (*wshandler.Websocket, error) {
// GetFeeByType returns an estimate of fee based on type of transaction
func (b *Binance) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !b.AllowAuthenticatedRequest() && // Todo check connection status
if (!b.AllowAuthenticatedRequest() || b.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}

View File

@@ -922,7 +922,16 @@ func (b *Bitfinex) CloseMarginFunding(swapID int64) (Offer, error) {
// SendHTTPRequest sends an unauthenticated request
func (b *Bitfinex) SendHTTPRequest(path string, result interface{}, verbose bool) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an autheticated http request and json
@@ -968,7 +977,8 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[
true,
true,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -306,7 +306,16 @@ func (b *Bitflyer) GetTradingCommission() {
// SendHTTPRequest sends an unauthenticated request
func (b *Bitflyer) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthHTTPRequest sends an authenticated HTTP request

View File

@@ -459,7 +459,16 @@ func (b *Bithumb) MarketSellOrder(currency string, units float64) (MarketSell, e
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bithumb) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to bithumb
@@ -503,7 +512,8 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, r
true,
true,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}

View File

@@ -775,14 +775,32 @@ func (b *Bitmex) SendHTTPRequest(path string, params Parameter, result interface
if err != nil {
return err
}
err = b.SendPayload(http.MethodGet, encodedPath, nil, nil, &respCheck, false, false, b.Verbose, b.HTTPDebugging)
err = b.SendPayload(http.MethodGet,
encodedPath,
nil,
nil,
&respCheck,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
return b.CaptureError(respCheck, result)
}
}
err := b.SendPayload(http.MethodGet, path, nil, nil, &respCheck, false, false, b.Verbose, b.HTTPDebugging)
err := b.SendPayload(http.MethodGet,
path,
nil,
nil,
&respCheck,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
@@ -834,7 +852,8 @@ func (b *Bitmex) SendAuthenticatedHTTPRequest(verb, path string, params Paramete
true,
false,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}

View File

@@ -1,6 +1,7 @@
package bitstamp
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@@ -68,7 +69,6 @@ func (b *Bitstamp) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
var err error
b.Balance, err = b.GetBalance()
if err != nil {
return 0, err
@@ -260,16 +260,15 @@ func (b *Bitstamp) GetEURUSDConversionRate() (EURUSDConversionRate, error) {
// GetBalance returns full balance of currency held on the exchange
func (b *Bitstamp) GetBalance() (Balances, error) {
balance := Balances{}
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bitstampAPIBalance)
return balance, b.SendHTTPRequest(path, &balance)
var balance Balances
return balance,
b.SendAuthenticatedHTTPRequest(bitstampAPIBalance, true, nil, &balance)
}
// GetUserTransactions returns an array of transactions
func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions, error) {
type Response struct {
Date int64 `json:"datetime"`
Date string `json:"datetime"`
TransID int64 `json:"id"`
Type int `json:"type,string"`
USD interface{} `json:"usd"`
@@ -282,12 +281,18 @@ func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions,
}
var response []Response
if currencyPair != "" {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions, true, url.Values{}, &response); err != nil {
if currencyPair == "" {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions,
true,
url.Values{},
&response); err != nil {
return nil, err
}
} else {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions+"/"+currencyPair, true, url.Values{}, &response); err != nil {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions+"/"+currencyPair,
true,
url.Values{},
&response); err != nil {
return nil, err
}
}
@@ -383,10 +388,11 @@ func (b *Bitstamp) PlaceOrder(currencyPair string, price, amount float64, buy, m
orderType = exchange.SellOrderSide.ToLower().ToString()
}
path := fmt.Sprintf("%s/%s", orderType, strings.ToLower(currencyPair))
var path string
if market {
path = fmt.Sprintf("%s/%s/%s", orderType, bitstampAPIMarket, strings.ToLower(currencyPair))
} else {
path = fmt.Sprintf("%s/%s", orderType, strings.ToLower(currencyPair))
}
return response,
@@ -546,28 +552,41 @@ func (b *Bitstamp) GetUnconfirmedBitcoinDeposits() ([]UnconfirmedBTCTransactions
// currency - which currency to transfer
// subaccount - name of account
// toMain - bool either to or from account
func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount string, toMain bool) (bool, error) {
func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount string, toMain bool) error {
var req = url.Values{}
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
req.Add("currency", currency)
if subAccount == "" {
return errors.New("missing subAccount parameter")
}
req.Add("subAccount", subAccount)
path := bitstampAPITransferToMain
if !toMain {
var path string
if toMain {
path = bitstampAPITransferToMain
} else {
path = bitstampAPITransferFromMain
}
err := b.SendAuthenticatedHTTPRequest(path, true, req, nil)
if err != nil {
return false, err
}
var resp interface{}
return true, nil
return b.SendAuthenticatedHTTPRequest(path, true, req, &resp)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bitstamp) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated request
@@ -603,15 +622,26 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
headers["Content-Type"] = "application/x-www-form-urlencoded"
encodedValues := values.Encode()
readerValues := strings.NewReader(encodedValues)
readerValues := bytes.NewBufferString(encodedValues)
interim := json.RawMessage{}
errCap := struct {
Error string `json:"error"`
Error string `json:"error"`
Status string `json:"status"`
Reason interface{} `json:"reason"`
}{}
err := b.SendPayload(http.MethodPost, path, headers, readerValues, &interim, true, true, b.Verbose, b.HTTPDebugging)
err := b.SendPayload(http.MethodPost,
path,
headers,
readerValues,
&interim,
true,
true,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
@@ -620,6 +650,21 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
if errCap.Error != "" {
return errors.New(errCap.Error)
}
if data, ok := errCap.Reason.(map[string][]string); ok {
var details string
for _, v := range data {
details += strings.Join(v, "")
}
return errors.New(details)
}
if data, ok := errCap.Reason.(string); ok {
return errors.New(data)
}
if errCap.Status != "" {
return errors.New(errCap.Status)
}
}
return common.JSONDecode(interim, result)

View File

@@ -0,0 +1,33 @@
//+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 bitstamp
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()
cfg.LoadConfig("../../testdata/configtest.json")
bitstampConfig, err := cfg.GetExchangeConfig("Bitstamp")
if err != nil {
log.Fatal("Test Failed - Bitstamp Setup() init error", err)
}
bitstampConfig.API.AuthenticatedSupport = true
bitstampConfig.API.Credentials.Key = apiKey
bitstampConfig.API.Credentials.Secret = apiSecret
bitstampConfig.API.Credentials.ClientID = customerID
b.SetDefaults()
b.Setup(bitstampConfig)
log.Printf(sharedtestvalues.LiveTesting, b.GetName(), b.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,46 @@
//+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 bitstamp
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/bitstamp/bitstamp.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
bitstampConfig, err := cfg.GetExchangeConfig("Bitstamp")
if err != nil {
log.Fatal("Test Failed - Bitstamp Setup() init error", err)
}
b.SkipAuthCheck = true
bitstampConfig.API.AuthenticatedSupport = true
bitstampConfig.API.Credentials.Key = apiKey
bitstampConfig.API.Credentials.Secret = apiSecret
bitstampConfig.API.Credentials.ClientID = customerID
b.SetDefaults()
b.Setup(bitstampConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
b.HTTPClient = newClient
b.API.Endpoints.URL = serverDetails + "/api"
log.Printf(sharedtestvalues.MockTesting, b.GetName(), b.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -4,7 +4,6 @@ import (
"net/url"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
@@ -19,40 +18,8 @@ const (
var b Bitstamp
func TestSetDefaults(t *testing.T) {
b.SetDefaults()
if b.Name != "Bitstamp" {
t.Error("Test Failed - SetDefaults() error")
}
if !b.Enabled {
t.Error("Test Failed - SetDefaults() error")
}
if !b.Verbose {
t.Error("Test Failed - SetDefaults() error")
}
if b.Websocket.IsEnabled() {
t.Error("Test Failed - SetDefaults() error")
}
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
bConfig, err := cfg.GetExchangeConfig("Bitstamp")
if err != nil {
t.Error("Test Failed - Bitstamp Setup() init error")
}
bConfig.API.Credentials.Key = apiKey
bConfig.API.Credentials.Secret = apiSecret
bConfig.API.Credentials.ClientID = customerID
b.Setup(bConfig)
if !b.IsEnabled() || b.API.AuthenticatedSupport ||
b.Verbose || b.Websocket.IsEnabled() || len(b.BaseCurrencies) < 1 {
t.Error("Test Failed - Bitstamp Setup values not set correctly")
}
func areTestAPIKeysSet() bool {
return b.ValidateAPICredentials()
}
func setFeeBuilder() *exchange.FeeBuilder {
@@ -66,53 +33,66 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.CryptocurrencyTradeFee,
feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -120,7 +100,9 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -128,7 +110,9 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -137,7 +121,9 @@ func TestGetFee(t *testing.T) {
feeBuilder.FeeType = exchange.InternationalBankDepositFee
feeBuilder.FiatCurrency = currency.HKD
if resp, err := b.GetFee(feeBuilder); resp != float64(7.5) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(7.5), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(7.5),
resp)
t.Error(err)
}
@@ -146,15 +132,15 @@ func TestGetFee(t *testing.T) {
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.HKD
if resp, err := b.GetFee(feeBuilder); resp != float64(15) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(15), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(15),
resp)
t.Error(err)
}
}
func TestCalculateTradingFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
b.Balance = Balances{}
t.Parallel()
b.Balance.BTCUSDFee = 1
b.Balance.BTCEURFee = 0
@@ -176,18 +162,16 @@ func TestCalculateTradingFee(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := b.GetTicker(currency.BTC.String()+currency.USD.String(), false)
if err != nil {
t.Error("Test Failed - GetTicker() error", err)
}
_, err = b.GetTicker(currency.BTC.String()+currency.USD.String(), true)
if err != nil {
t.Error("Test Failed - GetTicker() error", err)
}
}
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderbook(currency.BTC.String() + currency.USD.String())
if err != nil {
t.Error("Test Failed - GetOrderbook() error", err)
@@ -196,6 +180,7 @@ func TestGetOrderbook(t *testing.T) {
func TestGetTradingPairs(t *testing.T) {
t.Parallel()
_, err := b.GetTradingPairs()
if err != nil {
t.Error("Test Failed - GetTradingPairs() error", err)
@@ -204,6 +189,7 @@ func TestGetTradingPairs(t *testing.T) {
func TestGetTransactions(t *testing.T) {
t.Parallel()
value := url.Values{}
value.Set("time", "hour")
@@ -211,14 +197,11 @@ func TestGetTransactions(t *testing.T) {
if err != nil {
t.Error("Test Failed - GetTransactions() error", err)
}
_, err = b.GetTransactions("wigwham", value)
if err == nil {
t.Error("Test Failed - GetTransactions() error")
}
}
func TestGetEURUSDConversionRate(t *testing.T) {
t.Parallel()
_, err := b.GetEURUSDConversionRate()
if err != nil {
t.Error("Test Failed - GetEURUSDConversionRate() error", err)
@@ -227,21 +210,28 @@ func TestGetEURUSDConversionRate(t *testing.T) {
func TestGetBalance(t *testing.T) {
t.Parallel()
_, err := b.GetBalance()
if err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetBalance() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetBalance() error", err)
}
}
func TestGetUserTransactions(t *testing.T) {
t.Parallel()
_, err := b.GetUserTransactions("")
if err == nil {
t.Error("Test Failed - GetUserTransactions() error", err)
}
_, err = b.GetUserTransactions("btcusd")
if err == nil {
_, err := b.GetUserTransactions("btcusd")
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetUserTransactions() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetUserTransactions() error", err)
}
}
@@ -250,54 +240,27 @@ func TestGetOpenOrders(t *testing.T) {
t.Parallel()
_, err := b.GetOpenOrders("btcusd")
if err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetOpenOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetOpenOrders() error", err)
}
_, err = b.GetOpenOrders("wigwham")
if err == nil {
t.Error("Test Failed - GetOpenOrders() error")
}
}
func TestGetOrderStatus(t *testing.T) {
t.Parallel()
if !b.ValidateAPICredentials() {
t.Skip()
}
_, err := b.GetOrderStatus(1337)
if err == nil {
t.Error("Test Failed - GetOpenOrders() error")
}
}
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
resp, err := b.CancelExistingOrder(1337)
if err == nil || resp {
t.Error("Test Failed - CancelExistingOrder() error")
}
}
func TestCancelAllExistingOrders(t *testing.T) {
t.Parallel()
_, err := b.CancelAllExistingOrders()
if err == nil {
t.Error("Test Failed - CancelAllExistingOrders() error", err)
}
}
func TestPlaceOrder(t *testing.T) {
t.Parallel()
if !b.ValidateAPICredentials() {
t.Skip()
}
_, err := b.PlaceOrder("btcusd", 0.01, 1, true, true)
if err == nil {
t.Error("Test Failed - PlaceOrder() error")
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetOrderStatus() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting an error until a QA pass can be completed")
}
}
@@ -305,33 +268,13 @@ func TestGetWithdrawalRequests(t *testing.T) {
t.Parallel()
_, err := b.GetWithdrawalRequests(0)
if err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetWithdrawalRequests() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetWithdrawalRequests() error", err)
}
_, err = b.GetWithdrawalRequests(-1)
if err == nil {
t.Error("Test Failed - GetWithdrawalRequests() error")
}
}
func TestCryptoWithdrawal(t *testing.T) {
t.Parallel()
if !b.ValidateAPICredentials() {
t.Skip()
}
_, err := b.CryptoWithdrawal(0, "bla", "btc", "", true)
if err == nil {
t.Error("Test Failed - CryptoWithdrawal() error", err)
}
}
func TestGetBitcoinDepositAddress(t *testing.T) {
t.Parallel()
_, err := b.GetCryptoDepositAddress(currency.BTC)
if err == nil {
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
}
}
@@ -339,81 +282,90 @@ func TestGetUnconfirmedBitcoinDeposits(t *testing.T) {
t.Parallel()
_, err := b.GetUnconfirmedBitcoinDeposits()
if err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetUnconfirmedBitcoinDeposits() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetUnconfirmedBitcoinDeposits() error", err)
}
}
func TestTransferAccountBalance(t *testing.T) {
t.Parallel()
if !b.ValidateAPICredentials() {
if !areTestAPIKeysSet() && !mockTests {
t.Skip()
}
_, err := b.TransferAccountBalance(1, "", "", true)
if err == nil {
err := b.TransferAccountBalance(0.01, "btc", "testAccount", true)
if !mockTests && err != nil {
t.Error("Test Failed - TransferAccountBalance() error", err)
}
_, err = b.TransferAccountBalance(1, "btc", "", false)
if err == nil {
t.Error("Test Failed - TransferAccountBalance() error", err)
if mockTests && err == nil {
t.Error("Expecting an error until a QA pass can be completed")
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText +
" & " +
exchange.AutoWithdrawFiatText
withdrawPermissions := b.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := b.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get order history: %s", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
return b.ValidateAPICredentials()
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -429,18 +381,20 @@ func TestSubmitOrder(t *testing.T) {
ClientID: "meowOrder",
}
response, err := b.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests:
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -454,19 +408,20 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := b.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -480,11 +435,12 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := b.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Errorf("Could not cancel orders: %v", err)
}
@@ -494,6 +450,8 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := b.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
@@ -501,8 +459,11 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
@@ -513,24 +474,21 @@ func TestWithdraw(t *testing.T) {
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := b.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -555,19 +513,20 @@ func TestWithdrawFiat(t *testing.T) {
}
_, err := b.WithdrawFiatFunds(&withdrawFiatRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestWithdrawInternationalBank(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -598,24 +557,26 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
_, err := b.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestGetDepositAddress(t *testing.T) {
if areTestAPIKeysSet() && customerID != "" {
_, err := b.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress error", err)
}
} else {
_, err := b.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress error cannot be nil")
}
t.Parallel()
_, err := b.GetDepositAddress(currency.BTC, "")
switch {
case areTestAPIKeysSet() && customerID != "" && err != nil && !mockTests:
t.Error("Test Failed - GetDepositAddress error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetDepositAddress error cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - GetDepositAddress error", err)
}
}

View File

@@ -77,7 +77,7 @@ type Balances struct {
// UserTransactions holds user transaction information
type UserTransactions struct {
Date int64 `json:"datetime"`
Date string `json:"datetime"`
TransID int64 `json:"id"`
Type int `json:"type,string"`
USD float64 `json:"usd"`
@@ -125,15 +125,15 @@ type WithdrawalRequests struct {
// CryptoWithdrawalResponse response from a crypto withdrawal request
type CryptoWithdrawalResponse struct {
ID string `json:"id"`
Error string `json:"error"`
ID string `json:"id"`
Error map[string][]string `json:"error"`
}
// FIATWithdrawalResponse response from a fiat withdrawal request
type FIATWithdrawalResponse struct {
ID string `json:"id"`
Status string `json:"status"`
Reason string `json:"reason"`
ID string `json:"id"`
Status string `json:"status"`
Reason map[string][]string `json:"reason"`
}
// UnconfirmedBTCTransactions holds address information about unconfirmed

View File

@@ -235,7 +235,7 @@ func (b *Bitstamp) FetchTicker(p currency.Pair, assetType asset.Item) (ticker.Pr
// GetFeeByType returns an estimate of fee based on type of transaction
func (b *Bitstamp) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !b.AllowAuthenticatedRequest() && // Todo check connection status
if (!b.AllowAuthenticatedRequest() || b.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}
@@ -409,8 +409,12 @@ func (b *Bitstamp) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoW
if err != nil {
return "", err
}
if resp.Error != "" {
return "", errors.New(resp.Error)
if len(resp.Error) != 0 {
var details string
for _, v := range resp.Error {
details += strings.Join(v, "")
}
return "", errors.New(details)
}
return resp.ID, nil
@@ -427,7 +431,11 @@ func (b *Bitstamp) WithdrawFiatFunds(withdrawRequest *exchange.FiatWithdrawReque
return "", err
}
if resp.Status == errStr {
return "", errors.New(resp.Reason)
var details string
for _, v := range resp.Reason {
details += strings.Join(v, "")
}
return "", errors.New(details)
}
return resp.ID, nil
@@ -446,7 +454,11 @@ func (b *Bitstamp) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchang
return "", err
}
if resp.Status == errStr {
return "", errors.New(resp.Reason)
var details string
for _, v := range resp.Reason {
details += strings.Join(v, "")
}
return "", errors.New(details)
}
return resp.ID, nil
@@ -533,7 +545,12 @@ func (b *Bitstamp) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest)
quoteCurrency.String(),
b.GetPairFormat(asset.Spot, false).Delimiter)
}
orderDate := time.Unix(order.Date, 0)
orderDate, err := time.Parse("2006-01-02 15:04:05", order.Date)
if err != nil {
return nil, err
}
orders = append(orders, exchange.OrderDetail{
ID: fmt.Sprintf("%v", order.OrderID),
OrderDate: orderDate,

View File

@@ -427,7 +427,16 @@ func (b *Bittrex) GetDepositHistory(currency string) (WithdrawalHistory, error)
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bittrex) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated http request to a desired
@@ -448,7 +457,16 @@ func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, r
headers := make(map[string]string)
headers["apisign"] = crypto.HexEncodeToString(hmac)
return b.SendPayload(http.MethodGet, rawQuery, headers, nil, result, true, true, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
rawQuery,
headers,
nil,
result,
true,
true,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -364,7 +364,16 @@ func (b *BTCMarkets) WithdrawAUD(accountName, accountNumber, bankName, bsbNumber
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *BTCMarkets) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedRequest sends an authenticated HTTP request
@@ -415,7 +424,8 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data, result
true,
true,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -176,8 +176,16 @@ func (b *BTSE) GetFills(orderID, productID, before, after, limit string) (*Fille
// SendHTTPRequest sends an HTTP request to the desired endpoint
func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}) error {
p := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, endpoint)
return b.SendPayload(method, p, nil, nil, &result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(method,
fmt.Sprintf("%s/%s", b.API.Endpoints.URL, endpoint),
nil,
nil,
&result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the desired endpoint
@@ -203,8 +211,16 @@ func (b *BTSE) SendAuthenticatedHTTPRequest(method, endpoint string, req map[str
if b.Verbose {
log.Debugf(log.ExchangeSys, "Sending %s request to URL %s with params %s\n", method, p, string(payload))
}
return b.SendPayload(method, p, headers, strings.NewReader(string(payload)),
&result, true, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(method,
p,
headers,
strings.NewReader(string(payload)),
&result,
true,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -710,7 +710,16 @@ func (c *CoinbasePro) GetTrailingVolume() ([]Volume, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (c *CoinbasePro) SendHTTPRequest(path string, result interface{}) error {
return c.SendPayload(http.MethodGet, path, nil, nil, result, false, false, c.Verbose, c.HTTPDebugging)
return c.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
c.Verbose,
c.HTTPDebugging,
c.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP reque
@@ -751,7 +760,8 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(method, path string, params m
true,
true,
c.Verbose,
c.HTTPDebugging)
c.HTTPDebugging,
c.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -295,7 +295,7 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{
true,
c.Verbose,
c.HTTPDebugging,
)
c.HTTPRecording)
if err != nil {
return err
}

View File

@@ -462,6 +462,11 @@ func (e *Base) SetupDefaults(exch *config.ExchangeConfig) error {
// AllowAuthenticatedRequest checks to see if the required fields have been set before sending an authenticated
// API request
func (e *Base) AllowAuthenticatedRequest() bool {
// Skip auth check
if e.SkipAuthCheck {
return true
}
// Individual package usage, allow request if API credentials are valid a
// and without needing to set AuthenticatedSupport to true
if !e.LoadedByConfig && !e.ValidateAPICredentials() {

View File

@@ -304,12 +304,14 @@ type Base struct {
Enabled bool
Verbose bool
LoadedByConfig bool
SkipAuthCheck bool
API API
BaseCurrencies currency.Currencies
CurrencyPairs currency.PairsManager
Features Features
HTTPTimeout time.Duration
HTTPUserAgent string
HTTPRecording bool
HTTPDebugging bool
WebsocketResponseCheckTimeout time.Duration
WebsocketResponseMaxLimit time.Duration

View File

@@ -302,7 +302,16 @@ func (e *EXMO) GetWalletHistory(date int64) (WalletHistory, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (e *EXMO) SendHTTPRequest(path string, result interface{}) error {
return e.SendPayload(http.MethodGet, path, nil, nil, result, false, false, e.Verbose, e.HTTPDebugging)
return e.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
e.Verbose,
e.HTTPDebugging,
e.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -342,7 +351,8 @@ func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Va
true,
true,
e.Verbose,
e.HTTPDebugging)
e.HTTPDebugging,
e.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -306,7 +306,16 @@ 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 {
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, false, g.Verbose, g.HTTPDebugging)
return g.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
g.Verbose,
g.HTTPDebugging,
g.HTTPRecording)
}
// CancelAllExistingOrders all orders for a given symbol and side
@@ -407,7 +416,8 @@ func (g *Gateio) SendAuthenticatedHTTPRequest(method, endpoint, param string, re
true,
false,
g.Verbose,
g.HTTPDebugging)
g.HTTPDebugging,
g.HTTPRecording)
if err != nil {
return err
}

View File

@@ -53,11 +53,6 @@ const (
geminiRoleFundManager = "fundmanager"
)
var (
// Session manager
Session map[int]*Gemini
)
// Gemini is the overarching type across the Gemini package, create multiple
// instances with differing APIkeys for segregation of roles for authenticated
// requests & sessions by appending new sessions to the Session map using
@@ -71,31 +66,6 @@ type Gemini struct {
RequiresHeartBeat bool
}
// AddSession adds a new session to the gemini base
func AddSession(g *Gemini, sessionID int, apiKey, apiSecret, role string, needsHeartbeat, isSandbox bool) error {
if Session == nil {
Session = make(map[int]*Gemini)
}
_, ok := Session[sessionID]
if ok {
return errors.New("sessionID already being used")
}
g.API.Credentials.Key = apiKey
g.API.Credentials.Secret = apiSecret
g.Role = role
g.RequiresHeartBeat = needsHeartbeat
g.API.Endpoints.URL = geminiAPIURL
if isSandbox {
g.API.Endpoints.URL = geminiSandboxAPIURL
}
Session[sessionID] = g
return nil
}
// GetSymbols returns all available symbols for trading
func (g *Gemini) GetSymbols() ([]string, error) {
var symbols []string
@@ -155,9 +125,15 @@ func (g *Gemini) GetTicker(currencyPair string) (Ticker, error) {
// params - limit_bids or limit_asks [OPTIONAL] default 50, 0 returns all Values
// 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, geminiAPIVersion, geminiOrderbook, currencyPair), params)
orderbook := Orderbook{}
path := common.EncodeURLValues(
fmt.Sprintf("%s/v%s/%s/%s",
g.API.Endpoints.URL,
geminiAPIVersion,
geminiOrderbook,
currencyPair),
params)
var orderbook Orderbook
return orderbook, g.SendHTTPRequest(path, &orderbook)
}
@@ -202,20 +178,9 @@ func (g *Gemini) GetAuctionHistory(currencyPair string, params url.Values) ([]Au
return auctionHist, g.SendHTTPRequest(path, &auctionHist)
}
func (g *Gemini) isCorrectSession() error {
if g.Role != geminiRoleTrader {
return errors.New("incorrect role for APIKEY cannot use this function")
}
return nil
}
// NewOrder Only limit orders are supported through the API at present.
// returns order ID if successful
func (g *Gemini) NewOrder(symbol string, amount, price float64, side, orderType string) (int64, error) {
if err := g.isCorrectSession(); err != nil {
return 0, err
}
req := make(map[string]interface{})
req["symbol"] = symbol
req["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
@@ -300,6 +265,7 @@ func (g *Gemini) GetOrders() ([]Order, error) {
if err != nil {
return nil, err
}
switch r := response.(type) {
case orders:
return r.orders, nil
@@ -317,7 +283,7 @@ func (g *Gemini) GetTradeHistory(currencyPair string, timestamp int64) ([]TradeH
req := make(map[string]interface{})
req["symbol"] = currencyPair
if timestamp != 0 {
if timestamp > 0 {
req["timestamp"] = timestamp
}
@@ -406,7 +372,16 @@ func (g *Gemini) PostHeartbeat() (string, error) {
// SendHTTPRequest sends an unauthenticated request
func (g *Gemini) SendHTTPRequest(path string, result interface{}) error {
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, false, g.Verbose, g.HTTPDebugging)
return g.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
g.Verbose,
g.HTTPDebugging,
g.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the
@@ -416,7 +391,6 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name)
}
headers := make(map[string]string)
req := make(map[string]interface{})
req["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
req["nonce"] = g.Requester.GetNonce(true).String()
@@ -437,6 +411,7 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
PayloadBase64 := crypto.Base64Encode(PayloadJSON)
hmac := crypto.GetHMAC(crypto.HashSHA512_384, []byte(PayloadBase64), []byte(g.API.Credentials.Secret))
headers := make(map[string]string)
headers["Content-Length"] = "0"
headers["Content-Type"] = "text/plain"
headers["X-GEMINI-APIKEY"] = g.API.Credentials.Key
@@ -444,8 +419,16 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
headers["X-GEMINI-SIGNATURE"] = crypto.HexEncodeToString(hmac)
headers["Cache-Control"] = "no-cache"
return g.SendPayload(method, g.API.Endpoints.URL+"/v1/"+path, headers,
strings.NewReader(""), result, true, false, g.Verbose, g.HTTPDebugging)
return g.SendPayload(method,
g.API.Endpoints.URL+"/v1/"+path,
headers,
nil,
result,
true,
false,
g.Verbose,
g.HTTPDebugging,
g.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction
@@ -481,9 +464,9 @@ func getOfflineTradeFee(price, amount float64) float64 {
func calculateTradingFee(notionVolume *NotionalVolume, purchasePrice, amount float64, isMaker bool) float64 {
var volumeFee float64
if isMaker {
volumeFee = (float64(notionVolume.MakerFee) / 100)
volumeFee = (float64(notionVolume.APIMakerFeeBPS) / 10000)
} else {
volumeFee = (float64(notionVolume.TakerFee) / 100)
volumeFee = (float64(notionVolume.APITakerFeeBPS) / 10000)
}
return volumeFee * amount * purchasePrice

View File

@@ -0,0 +1,33 @@
//+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 gemini
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()
cfg.LoadConfig("../../testdata/configtest.json")
geminiConfig, err := cfg.GetExchangeConfig("Gemini")
if err != nil {
log.Fatal("Test Failed - Gemini Setup() init error", err)
}
geminiConfig.API.AuthenticatedSupport = true
geminiConfig.API.Credentials.Key = apiKey
geminiConfig.API.Credentials.Secret = apiSecret
g.SetDefaults()
g.Setup(geminiConfig)
g.API.Endpoints.URL = geminiSandboxAPIURL
log.Printf(sharedtestvalues.LiveTesting, g.GetName(), g.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,45 @@
//+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 gemini
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/gemini/gemini.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
geminiConfig, err := cfg.GetExchangeConfig("Gemini")
if err != nil {
log.Fatal("Test Failed - Mock server error", err)
}
g.SkipAuthCheck = true
geminiConfig.API.AuthenticatedSupport = true
geminiConfig.API.Credentials.Key = apiKey
geminiConfig.API.Credentials.Secret = apiSecret
g.SetDefaults()
g.Setup(geminiConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockFile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
g.HTTPClient = newClient
g.API.Endpoints.URL = serverDetails
log.Printf(sharedtestvalues.MockTesting, g.GetName(), g.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
@@ -15,75 +14,21 @@ import (
)
// Please enter sandbox API keys & assigned roles for better testing procedures
const (
apiKey1 = ""
apiSecret1 = ""
apiKeyRole1 = ""
sessionHeartBeat1 = false
apiKey2 = ""
apiSecret2 = ""
apiKeyRole2 = ""
sessionHeartBeat2 = false
apiKey = ""
apiSecret = ""
apiKeyRole = ""
sessionHeartBeat = false
canManipulateRealOrders = false
)
func TestAddSession(t *testing.T) {
var g1 Gemini
if Session[1] == nil {
err := AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, true)
if err != nil {
t.Error("Test failed - AddSession() error", err)
}
err = AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, true)
if err == nil {
t.Error("Test failed - AddSession() error", err)
}
}
const testCurrency = "btcusd"
if len(Session) <= 1 {
var g2 Gemini
err := AddSession(&g2, 2, apiKey2, apiSecret2, apiKeyRole2, false, true)
if err != nil {
t.Error("Test failed - AddSession() error", err)
}
}
}
func TestSetDefaults(t *testing.T) {
Session[1].SetDefaults()
Session[2].SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
geminiConfig, err := cfg.GetExchangeConfig("Gemini")
if err != nil {
t.Error("Test Failed - Gemini Setup() init error")
}
geminiConfig.API.AuthenticatedSupport = true
geminiConfig.API.AuthenticatedWebsocketSupport = true
Session[1].Setup(geminiConfig)
Session[2].Setup(geminiConfig)
Session[1].API.Credentials.Key = apiKey1
Session[1].API.Credentials.Secret = apiSecret1
Session[2].API.Credentials.Key = apiKey2
Session[2].API.Credentials.Secret = apiSecret2
Session[1].API.Endpoints.URL = geminiSandboxAPIURL
Session[2].API.Endpoints.URL = geminiSandboxAPIURL
}
var g Gemini
func TestGetSymbols(t *testing.T) {
t.Parallel()
_, err := Session[1].GetSymbols()
_, err := g.GetSymbols()
if err != nil {
t.Error("Test Failed - GetSymbols() error", err)
}
@@ -91,11 +36,11 @@ func TestGetSymbols(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := Session[2].GetTicker("BTCUSD")
_, err := g.GetTicker("BTCUSD")
if err != nil {
t.Error("Test Failed - GetTicker() error", err)
}
_, err = Session[1].GetTicker("bla")
_, err = g.GetTicker("bla")
if err == nil {
t.Error("Test Failed - GetTicker() error", err)
}
@@ -103,7 +48,7 @@ func TestGetTicker(t *testing.T) {
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := Session[1].GetOrderbook("btcusd", url.Values{})
_, err := g.GetOrderbook(testCurrency, url.Values{})
if err != nil {
t.Error("Test Failed - GetOrderbook() error", err)
}
@@ -111,25 +56,25 @@ func TestGetOrderbook(t *testing.T) {
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := Session[2].GetTrades("btcusd", url.Values{})
_, err := g.GetTrades(testCurrency, url.Values{})
if err != nil {
t.Error("Test Failed - GetTrades() error", err)
}
}
func TestGetNotionalVolume(t *testing.T) {
if apiKey2 != "" && apiSecret2 != "" {
t.Parallel()
_, err := Session[2].GetNotionalVolume()
if err != nil {
t.Error("Test Failed - GetNotionalVolume() error", err)
}
t.Parallel()
_, err := g.GetNotionalVolume()
if err != nil && mockTests {
t.Error("Test Failed - GetNotionalVolume() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetNotionalVolume() error cannot be nil")
}
}
func TestGetAuction(t *testing.T) {
t.Parallel()
_, err := Session[1].GetAuction("btcusd")
_, err := g.GetAuction(testCurrency)
if err != nil {
t.Error("Test Failed - GetAuction() error", err)
}
@@ -137,7 +82,7 @@ func TestGetAuction(t *testing.T) {
func TestGetAuctionHistory(t *testing.T) {
t.Parallel()
_, err := Session[2].GetAuctionHistory("btcusd", url.Values{})
_, err := g.GetAuctionHistory(testCurrency, url.Values{})
if err != nil {
t.Error("Test Failed - GetAuctionHistory() error", err)
}
@@ -145,81 +90,87 @@ func TestGetAuctionHistory(t *testing.T) {
func TestNewOrder(t *testing.T) {
t.Parallel()
_, err := Session[1].NewOrder("btcusd", 1, 4500,
exchange.BuyOrderSide.ToLower().ToString(), "exchange limit")
if err == nil {
t.Error("Test Failed - NewOrder() error", err)
}
_, err = Session[2].NewOrder("btcusd", 1, 4500,
exchange.BuyOrderSide.ToLower().ToString(), "exchange limit")
if err == nil {
_, err := g.NewOrder(testCurrency, 1, 9000, "buy", "exchange limit")
if err != nil && mockTests {
t.Error("Test Failed - NewOrder() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - NewOrder() error cannot be nil")
}
}
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
_, err := Session[1].CancelExistingOrder(1337)
if err == nil {
_, err := g.CancelExistingOrder(265555413)
if err != nil && mockTests {
t.Error("Test Failed - CancelExistingOrder() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - CancelExistingOrder() error cannot be nil")
}
}
func TestCancelExistingOrders(t *testing.T) {
t.Parallel()
_, err := Session[1].CancelExistingOrders(false)
if err == nil {
t.Error("Test Failed - CancelExistingOrders() error", err)
}
_, err = Session[2].CancelExistingOrders(true)
if err == nil {
_, err := g.CancelExistingOrders(false)
if err != nil && mockTests {
t.Error("Test Failed - CancelExistingOrders() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - CancelExistingOrders() error cannot be nil")
}
}
func TestGetOrderStatus(t *testing.T) {
t.Parallel()
_, err := Session[2].GetOrderStatus(1337)
if err == nil {
_, err := g.GetOrderStatus(265563260)
if err != nil && mockTests {
t.Error("Test Failed - GetOrderStatus() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetOrderStatus() error cannot be nil")
}
}
func TestGetOrders(t *testing.T) {
t.Parallel()
_, err := Session[1].GetOrders()
if err == nil {
_, err := g.GetOrders()
if err != nil && mockTests {
t.Error("Test Failed - GetOrders() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetOrders() error cannot be nil")
}
}
func TestGetTradeHistory(t *testing.T) {
t.Parallel()
_, err := Session[1].GetTradeHistory("btcusd", 0)
if err == nil {
_, err := g.GetTradeHistory(testCurrency, 0)
if err != nil && mockTests {
t.Error("Test Failed - GetTradeHistory() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetTradeHistory() error cannot be nil")
}
}
func TestGetTradeVolume(t *testing.T) {
t.Parallel()
_, err := Session[2].GetTradeVolume()
if err == nil {
_, err := g.GetTradeVolume()
if err != nil && mockTests {
t.Error("Test Failed - GetTradeVolume() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetTradeVolume() error cannot be nil")
}
}
func TestGetBalances(t *testing.T) {
t.Parallel()
_, err := Session[1].GetBalances()
if err == nil {
_, err := g.GetBalances()
if err != nil && mockTests {
t.Error("Test Failed - GetBalances() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetBalances() error cannot be nil")
}
}
func TestGetCryptoDepositAddress(t *testing.T) {
t.Parallel()
_, err := Session[1].GetCryptoDepositAddress("LOL123", "btc")
_, err := g.GetCryptoDepositAddress("LOL123", "btc")
if err == nil {
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
}
@@ -227,7 +178,7 @@ func TestGetCryptoDepositAddress(t *testing.T) {
func TestWithdrawCrypto(t *testing.T) {
t.Parallel()
_, err := Session[1].WithdrawCrypto("LOL123", "btc", 1)
_, err := g.WithdrawCrypto("LOL123", "btc", 1)
if err == nil {
t.Error("Test Failed - WithdrawCrypto() error", err)
}
@@ -235,9 +186,11 @@ func TestWithdrawCrypto(t *testing.T) {
func TestPostHeartbeat(t *testing.T) {
t.Parallel()
_, err := Session[2].PostHeartbeat()
if err == nil {
_, err := g.PostHeartbeat()
if err != nil && mockTests {
t.Error("Test Failed - PostHeartbeat() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - PostHeartbeat() error cannot be nil")
}
}
@@ -256,61 +209,75 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
Session[1].GetFeeByType(feeBuilder)
if apiKey1 == "" || apiSecret1 == "" {
g.GetFeeByType(feeBuilder)
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.CryptocurrencyTradeFee,
feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
if apiKey1 != "" && apiSecret1 != "" {
if areTestAPIKeysSet() || mockTests {
// CryptocurrencyTradeFee Basic
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0.01) || err != nil {
if resp, err := g.GetFee(feeBuilder); resp != float64(0.0035) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.0035),
resp)
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.01), resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(100) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(100), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(3500) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(3500),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0.01) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.01), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.001),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
}
// CryptocurrencyWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -318,24 +285,30 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.Pair.Base = currency.NewCode("hello")
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// CyptocurrencyDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// InternationalBankDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -343,74 +316,78 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.USD
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText +
" & " +
exchange.AutoWithdrawCryptoWithSetupText +
" & " +
exchange.WithdrawFiatViaWebsiteOnlyText
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := Session[1].FormatWithdrawPermissions()
withdrawPermissions := g.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC, currency.BTC)},
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{
currency.NewPair(currency.LTC, currency.BTC),
},
}
_, err := Session[1].GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
_, err := g.GetActiveOrders(&getOrdersRequest)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC, currency.BTC)},
}
_, err := Session[1].GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
_, err := g.GetOrderHistory(&getOrdersRequest)
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case err != nil && mockTests:
t.Errorf("Could not get order history: %s", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
return Session[1].ValidateAPICredentials()
return g.ValidateAPICredentials()
}
func TestSubmitOrder(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -422,51 +399,46 @@ func TestSubmitOrder(t *testing.T) {
},
OrderSide: exchange.BuyOrderSide,
OrderType: exchange.LimitOrderType,
Price: 1,
Price: 10,
Amount: 1,
ClientID: "meowOrder",
ClientID: "1234234",
}
response, err := Session[1].SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
response, err := g.SubmitOrder(orderSubmission)
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced):
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Order failed to be placed: %v", err)
}
}
func TestCancelExchangeOrder(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
OrderID: "266029865",
}
err := Session[1].CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
err := g.CancelOrder(orderCancellation)
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case err != nil && mockTests:
t.Errorf("Could not cancel orders: %v", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -479,12 +451,13 @@ func TestCancelAllExchangeOrders(t *testing.T) {
CurrencyPair: currencyPair,
}
resp, err := Session[1].CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
resp, err := g.CancelAllOrders(orderCancellation)
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Errorf("Could not cancel orders: %v", err)
}
@@ -494,16 +467,15 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
_, err := Session[1].ModifyOrder(&exchange.ModifyOrder{})
t.Parallel()
_, err := g.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
}
}
func TestWithdraw(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -513,53 +485,55 @@ func TestWithdraw(t *testing.T) {
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := Session[1].WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
_, err := g.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
}
if areTestAPIKeysSet() && err == nil && mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
}
}
func TestWithdrawFiat(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := Session[1].WithdrawFiatFunds(&withdrawFiatRequest)
_, err := g.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := Session[1].WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
_, err := g.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestGetDepositAddress(t *testing.T) {
_, err := Session[1].GetDepositAddress(currency.BTC, "")
t.Parallel()
_, err := g.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress error cannot be nil")
}
@@ -567,13 +541,12 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
g := Session[1]
t.Parallel()
g.API.Endpoints.WebsocketURL = geminiWebsocketSandboxEndpoint
if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
if !g.Websocket.IsEnabled() &&
!g.API.AuthenticatedWebsocketSupport ||
!areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
var dialer websocket.Dialer

View File

@@ -145,16 +145,23 @@ type TradeVolume struct {
SellTakerCount float64 `json:"sell_taker_count"`
}
// NotionalVolume api call for fees
// NotionalVolume api call for fees, all return fee amounts are in basis points
type NotionalVolume struct {
MakerFee int64 `json:"maker_fee_bps"`
TakerFee int64 `json:"taker_fee_bps"`
AuctionFee int64 `json:"auction_fee_bps"`
ThirtyDayVolume float64 `json:"notional_30d_volume"`
LastedUpdated int64 `json:"last_updated_ms"`
AccountID int64 `json:"account_id"`
Date string `json:"date"`
APIAuctionFeeBPS int64 `json:"api_auction_fee_bps"`
APIMakerFeeBPS int64 `json:"api_maker_fee_bps"`
APITakerFeeBPS int64 `json:"api_taker_fee_bps"`
BlockMakerFeeBPS int64 `json:"block_maker_fee_bps"`
BlockTakerFeeBPS int64 `json:"block_taker_fee_bps"`
FixAuctionFeeBPS int64 `json:"fix_auction_fee_bps"`
FixMakerFeeBPS int64 `json:"fix_maker_fee_bps"`
FixTakerFeeBPS int64 `json:"fix_taker_fee_bps"`
OneDayNotionalVolumes []OneDayNotionalVolume `json:"notional_1d_volume"`
ThirtyDayVolume float64 `json:"notional_30d_volume"`
WebAuctionFeeBPS int64 `json:"web_auction_fee_bps"`
WebMakerFeeBPS int64 `json:"web_maker_fee_bps"`
WebTakerFeeBPS int64 `json:"web_taker_fee_bps"`
LastedUpdated int64 `json:"last_updated_ms"`
Date string `json:"date"`
}
// OneDayNotionalVolume Contains the notioanl volume for a single day

View File

@@ -305,11 +305,16 @@ func (g *Gemini) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOr
return submitOrderResponse, err
}
response, err := g.NewOrder(order.Pair.String(),
if order.OrderType != exchange.LimitOrderType {
return submitOrderResponse, errors.New("only limit orders are enabled through this exchange")
}
response, err := g.NewOrder(
g.FormatExchangeCurrency(order.Pair, asset.Spot).String(),
order.Amount,
order.Price,
order.OrderSide.ToString(),
order.OrderType.ToString())
"exchange limit")
if response > 0 {
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
}
@@ -401,7 +406,7 @@ func (g *Gemini) GetWebsocket() (*wshandler.Websocket, error) {
// GetFeeByType returns an estimate of fee based on type of transaction
func (g *Gemini) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !g.AllowAuthenticatedRequest() && // Todo check connection status
if (!g.AllowAuthenticatedRequest() || g.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}

View File

@@ -508,7 +508,16 @@ 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 {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated http request
@@ -530,7 +539,8 @@ func (h *HitBTC) SendAuthenticatedHTTPRequest(method, endpoint string, values ur
true,
false,
h.Verbose,
h.HTTPDebugging)
h.HTTPDebugging,
h.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -776,7 +776,16 @@ func (h *HUOBI) CancelWithdraw(withdrawID int64) (int64, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBI) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
@@ -852,7 +861,16 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
body = encoded
}
return h.SendPayload(method, urlPath, headers, bytes.NewReader(body), result, true, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewReader(body),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -773,7 +773,16 @@ func (h *HUOBIHADAX) CancelWithdraw(withdrawID int64) (int64, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBIHADAX) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPPostRequest sends authenticated requests to the HUOBI API
@@ -801,7 +810,16 @@ func (h *HUOBIHADAX) SendAuthenticatedHTTPPostRequest(method, endpoint, postBody
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.API.Endpoints.URL, endpoint),
signatureParams)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(postBodyValues), result, true, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewBufferString(postBodyValues),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
@@ -827,7 +845,16 @@ func (h *HUOBIHADAX) SendAuthenticatedHTTPRequest(method, endpoint string, value
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.API.Endpoints.URL, endpoint),
values)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(""), result, true, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewBufferString(""),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -276,7 +276,16 @@ func (i *ItBit) WalletTransfer(walletID, sourceWallet, destWallet string, amount
// SendHTTPRequest sends an unauthenticated HTTP request
func (i *ItBit) SendHTTPRequest(path string, result interface{}) error {
return i.SendPayload(http.MethodGet, path, nil, nil, result, false, false, i.Verbose, i.HTTPDebugging)
return i.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
i.Verbose,
i.HTTPDebugging,
i.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated request to itBit
@@ -331,7 +340,16 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method, path string, params map[str
RequestID string `json:"requestId"`
}{}
err = i.SendPayload(method, urlPath, headers, bytes.NewBuffer(PayloadJSON), &intermediary, true, true, i.Verbose, i.HTTPDebugging)
err = i.SendPayload(method,
urlPath,
headers,
bytes.NewBuffer(PayloadJSON),
&intermediary,
true,
true,
i.Verbose,
i.HTTPDebugging,
i.HTTPRecording)
if err != nil {
return err
}

View File

@@ -846,7 +846,16 @@ func GetError(apiErrors []string) error {
// SendHTTPRequest sends an unauthenticated HTTP requests
func (k *Kraken) SendHTTPRequest(path string, result interface{}) error {
return k.SendPayload(http.MethodGet, path, nil, nil, result, false, false, k.Verbose, k.HTTPDebugging)
return k.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
k.Verbose,
k.HTTPDebugging,
k.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -883,7 +892,8 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
true,
true,
k.Verbose,
k.HTTPDebugging)
k.HTTPDebugging,
k.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -251,7 +251,16 @@ func (l *LakeBTC) CreateWithdraw(amount float64, accountID string) (Withdraw, er
// SendHTTPRequest sends an unauthenticated http request
func (l *LakeBTC) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, false, l.Verbose, l.HTTPDebugging)
return l.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an autheticated HTTP request to a LakeBTC
@@ -284,8 +293,16 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
headers["Authorization"] = "Basic " + crypto.Base64Encode([]byte(l.API.Credentials.Key+":"+crypto.HexEncodeToString(hmac)))
headers["Content-Type"] = "application/json-rpc"
return l.SendPayload(http.MethodPost, l.API.Endpoints.URL, headers,
strings.NewReader(string(data)), result, true, true, l.Verbose, l.HTTPDebugging)
return l.SendPayload(http.MethodPost,
l.API.Endpoints.URL,
headers,
strings.NewReader(string(data)),
result,
true,
true,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

133
exchanges/lbank/README.md Normal file
View File

@@ -0,0 +1,133 @@
# GoCryptoTrader package Lbank
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://travis-ci.org/thrasher-corp/gocryptotrader.svg?branch=master)](https://travis-ci.org/thrasher-corp/gocryptotrader)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/lbank)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This lbank package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTQyYjIxNGVhMWU5MDZlOGYzMmE0NTJmM2MzYWY5NGMzMmM4MzUwNTBjZTEzNjIwODM5NDcxODQwZDljMGQyNGY)
## Lbank Exchange
### Current Features
+ REST Support
### How to enable
+ [Enable via configuration](https://githul.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
+ Individual package example below:
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### How to do REST public/private calls
+ If enabled via "configuration".json file the exchange will be added to the
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
the wrapper interface functions for accessing exchange data. View routines.go
for an example of integration usage with GoCryptoTrader. Rudimentary example
below:
main.go
```go
var l exchange.IBotExchange
for i := range bot.exchanges {
if bot.exchanges[i].GetName() == "Lbank" {
l = bot.exchanges[i]
}
}
// Public calls - wrapper functions
// Fetches current ticker information
tick, err := l.GetTickerPrice()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := l.GetOrderbookEx()
if err != nil {
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
// set and AuthenticatedAPISupport is set to true
// Fetches current account information
accountInfo, err := l.GetAccountInfo()
if err != nil {
// Handle error
}
```
+ If enabled via individually importing package, rudimentary example below:
```go
// Public calls
// Fetches current ticker information
ticker, err := l.GetTicker()
if err != nil {
// Handle error
}
// Fetches current orderbook information
ob, err := l.GetOrderBook()
if err != nil {
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
// AuthenticatedAPISupport is set to true
// GetUserInfo returns account info
accountInfo, err := l.GetUserInfo(...)
if err != nil {
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := l.Trade(...)
if err != nil {
// Handle error
}
```
### Please click GoDocs chevron above to view current GoDoc information for this package
## Contribution
Please feel free to submit any pull requests or suggest any desired features to be added.
When submitting a PR, please abide by our coding guidelines:
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
+ Pull requests need to be based on and opened against the `master` branch.
## Donations
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

562
exchanges/lbank/lbank.go Normal file
View File

@@ -0,0 +1,562 @@
package lbank
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
gctcrypto "github.com/thrasher-corp/gocryptotrader/common/crypto"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
)
// Lbank is the overarching type across this package
type Lbank struct {
exchange.Base
privateKey *rsa.PrivateKey
WebsocketConn *wshandler.WebsocketConnection
}
const (
lbankAPIURL = "https://api.lbkex.com"
lbankAPIVersion = "1"
lbankAuthRateLimit = 0
lbankUnAuthRateLimit = 0
lbankFeeNotFound = 0.0
// Public endpoints
lbankTicker = "ticker.do"
lbankCurrencyPairs = "currencyPairs.do"
lbankMarketDepths = "depth.do"
lbankTrades = "trades.do"
lbankKlines = "kline.do"
lbankPairInfo = "accuracy.do"
lbankUSD2CNYRate = "usdToCny.do"
lbankWithdrawConfig = "withdrawConfigs.do"
// Authenticated endpoints
lbankUserInfo = "user_info.do"
lbankPlaceOrder = "create_order.do"
lbankCancelOrder = "cancel_order.do"
lbankQueryOrder = "orders_info.do"
lbankQueryHistoryOrder = "orders_info_history.do"
lbankOrderTransactionDetails = "order_transaction_detail.do"
lbankPastTransactions = "transaction_history.do"
lbankOpeningOrders = "orders_info_no_deal.do"
lbankWithdrawalRecords = "withdraws.do"
lbankWithdraw = "withdraw.do"
lbankRevokeWithdraw = "withdrawCancel.do"
)
// GetTicker returns a ticker for the specified symbol
// symbol: eth_btc
func (l *Lbank) GetTicker(symbol string) (TickerResponse, error) {
var t TickerResponse
params := url.Values{}
params.Set("symbol", symbol)
path := fmt.Sprintf("%s/v%s/%s?%s", l.API.Endpoints.URL, lbankAPIVersion, lbankTicker, params.Encode())
return t, l.SendHTTPRequest(path, &t)
}
// GetCurrencyPairs returns a list of supported currency pairs by the exchange
func (l *Lbank) GetCurrencyPairs() ([]string, error) {
path := fmt.Sprintf("%s/v%s/%s", l.API.Endpoints.URL, lbankAPIVersion,
lbankCurrencyPairs)
var result []string
return result, l.SendHTTPRequest(path, &result)
}
// GetMarketDepths returns arrays of asks, bids and timestamp
func (l *Lbank) GetMarketDepths(symbol, size, merge string) (MarketDepthResponse, error) {
var m MarketDepthResponse
params := url.Values{}
params.Set("symbol", symbol)
params.Set("size", size)
params.Set("merge", merge)
path := fmt.Sprintf("%s/v%s/%s?%s", l.API.Endpoints.URL, lbankAPIVersion, lbankMarketDepths, params.Encode())
return m, l.SendHTTPRequest(path, &m)
}
// GetTrades returns an array of available trades regarding a particular exchange
func (l *Lbank) GetTrades(symbol, size, time string) ([]TradeResponse, error) {
var g []TradeResponse
params := url.Values{}
params.Set("symbol", symbol)
params.Set("size", size)
params.Set("time", time)
path := fmt.Sprintf("%s/v%s/%s?%s", l.API.Endpoints.URL, lbankAPIVersion, lbankTrades, params.Encode())
return g, l.SendHTTPRequest(path, &g)
}
// GetKlines returns kline data
func (l *Lbank) GetKlines(symbol, size, klineType, time string) ([]KlineResponse, error) {
var klineTemp interface{}
var k []KlineResponse
params := url.Values{}
params.Set("symbol", symbol)
params.Set("size", size)
params.Set("type", klineType)
params.Set("time", time)
path := fmt.Sprintf("%s/v%s/%s?%s", l.API.Endpoints.URL, lbankAPIVersion, lbankKlines, params.Encode())
err := l.SendHTTPRequest(path, &klineTemp)
if err != nil {
return k, err
}
resp, ok := klineTemp.([]interface{})
if !ok {
return nil, errors.New("response received is invalid")
}
for i := range resp {
resp2, ok := resp[i].([]interface{})
if !ok {
return nil, errors.New("response received is invalid")
}
var tempResp KlineResponse
for x := range resp2 {
switch x {
case 0:
tempResp.TimeStamp = int64(resp2[x].(float64))
case 1:
if val, ok := resp2[x].(int64); ok {
tempResp.OpenPrice = float64(val)
} else {
tempResp.OpenPrice = resp2[x].(float64)
}
case 2:
if val, ok := resp2[x].(int64); ok {
tempResp.HigestPrice = float64(val)
} else {
tempResp.HigestPrice = resp2[x].(float64)
}
case 3:
if val, ok := resp2[x].(int64); ok {
tempResp.LowestPrice = float64(val)
} else {
tempResp.LowestPrice = resp2[x].(float64)
}
case 4:
if val, ok := resp2[x].(int64); ok {
tempResp.ClosePrice = float64(val)
} else {
tempResp.ClosePrice = resp2[x].(float64)
}
case 5:
if val, ok := resp2[x].(int64); ok {
tempResp.TradingVolume = float64(val)
} else {
tempResp.TradingVolume = resp2[x].(float64)
}
}
}
k = append(k, tempResp)
}
return k, nil
}
// GetUserInfo gets users account info
func (l *Lbank) GetUserInfo() (InfoFinalResponse, error) {
var resp InfoFinalResponse
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankUserInfo)
err := l.SendAuthHTTPRequest(http.MethodPost, path, nil, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// CreateOrder creates an order
func (l *Lbank) CreateOrder(pair, side string, amount, price float64) (CreateOrderResponse, error) {
var resp CreateOrderResponse
if !strings.EqualFold(side, "buy") && !strings.EqualFold(side, "sell") {
return resp, errors.New("side type invalid can only be 'buy' or 'sell'")
}
if amount <= 0 {
return resp, errors.New("amount can't be smaller than or equal to 0")
}
if price <= 0 {
return resp, errors.New("price can't be smaller than or equal to 0")
}
params := url.Values{}
params.Set("symbol", pair)
params.Set("type", strings.ToLower(side))
params.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankPlaceOrder)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// RemoveOrder cancels a given order
func (l *Lbank) RemoveOrder(pair, orderID string) (RemoveOrderResponse, error) {
var resp RemoveOrderResponse
params := url.Values{}
params.Set("symbol", pair)
params.Set("order_id", orderID)
path := fmt.Sprintf("%s/v%s/%s", l.API.Endpoints.URL, lbankAPIVersion, lbankCancelOrder)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// QueryOrder finds out information about orders (can pass up to 3 comma separated values to this)
// Lbank returns an empty string as their []OrderResponse instead of returning an empty array, so when len(tempResp.Orders) > 2 its not empty and should be unmarshalled separately
func (l *Lbank) QueryOrder(pair, orderIDs string) (QueryOrderFinalResponse, error) {
var resp QueryOrderFinalResponse
var tempResp QueryOrderResponse
params := url.Values{}
params.Set("symbol", pair)
params.Set("order_id", orderIDs)
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankQueryOrder)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &tempResp)
if err != nil {
return resp, err
}
var totalOrders []OrderResponse
if len(tempResp.Orders) > 2 {
err = json.Unmarshal(tempResp.Orders, &totalOrders)
if err != nil {
return resp, err
}
}
resp.ErrCapture = tempResp.ErrCapture
resp.Orders = totalOrders
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// QueryOrderHistory finds order info in the past 2 days
// Lbank returns an empty string as their []OrderResponse instead of returning an empty array, so when len(tempResp.Orders) > 2 its not empty and should be unmarshalled separately
func (l *Lbank) QueryOrderHistory(pair, pageNumber, pageLength string) (OrderHistoryFinalResponse, error) {
var resp OrderHistoryFinalResponse
var tempResp OrderHistoryResponse
params := url.Values{}
params.Set("symbol", pair)
params.Set("current_page", pageNumber)
params.Set("page_length", pageLength)
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankQueryHistoryOrder)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &tempResp)
if err != nil {
return resp, err
}
var totalOrders []OrderResponse
if len(tempResp.Orders) > 2 {
err = json.Unmarshal(tempResp.Orders, &totalOrders)
if err != nil {
return resp, err
}
}
resp.ErrCapture = tempResp.ErrCapture
resp.PageLength = tempResp.PageLength
resp.Orders = totalOrders
resp.CurrentPage = tempResp.CurrentPage
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// GetPairInfo finds information about all trading pairs
func (l *Lbank) GetPairInfo() ([]PairInfoResponse, error) {
var resp []PairInfoResponse
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankPairInfo)
return resp, l.SendHTTPRequest(path, &resp)
}
// OrderTransactionDetails gets info about transactions
func (l *Lbank) OrderTransactionDetails(symbol, orderID string) (TransactionHistoryResp, error) {
var resp TransactionHistoryResp
params := url.Values{}
params.Set("symbol", symbol)
params.Set("order_id", orderID)
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankOrderTransactionDetails)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// TransactionHistory stores info about transactions
func (l *Lbank) TransactionHistory(symbol, transactionType, startDate, endDate, from, direct, size string) (TransactionHistoryResp, error) {
var resp TransactionHistoryResp
params := url.Values{}
params.Set("symbol", symbol)
params.Set("type", transactionType)
params.Set("start_date", startDate)
params.Set("end_date", endDate)
params.Set("from", from)
params.Set("direct", direct)
params.Set("size", size)
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankPastTransactions)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// GetOpenOrders gets opening orders
// Lbank returns an empty string as their []OrderResponse instead of returning an empty array, so when len(tempResp.Orders) > 2 its not empty and should be unmarshalled separately
func (l *Lbank) GetOpenOrders(pair, pageNumber, pageLength string) (OpenOrderFinalResponse, error) {
var resp OpenOrderFinalResponse
var tempResp OpenOrderResponse
params := url.Values{}
params.Set("symbol", pair)
params.Set("current_page", pageNumber)
params.Set("page_length", pageLength)
path := fmt.Sprintf("%s/v%s/%s", l.API.Endpoints.URL, lbankAPIVersion, lbankOpeningOrders)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &tempResp)
if err != nil {
return resp, err
}
var totalOrders []OrderResponse
if len(tempResp.Orders) > 2 {
err = json.Unmarshal(tempResp.Orders, &totalOrders)
if err != nil {
return resp, err
}
}
resp.ErrCapture = tempResp.ErrCapture
resp.PageLength = tempResp.PageLength
resp.PageNumber = tempResp.PageNumber
resp.Orders = totalOrders
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// USD2RMBRate finds USD-CNY Rate
func (l *Lbank) USD2RMBRate() (ExchangeRateResponse, error) {
var resp ExchangeRateResponse
path := fmt.Sprintf("%s/v%s/%s", l.API.Endpoints.URL, lbankAPIVersion, lbankUSD2CNYRate)
return resp, l.SendHTTPRequest(path, &resp)
}
// GetWithdrawConfig gets information about withdrawals
func (l *Lbank) GetWithdrawConfig(assetCode string) ([]WithdrawConfigResponse, error) {
var resp []WithdrawConfigResponse
params := url.Values{}
params.Set("assetCode", assetCode)
path := fmt.Sprintf("%s/v%s/%s?%s", l.API.Endpoints.URL, lbankAPIVersion, lbankWithdrawConfig, params.Encode())
return resp, l.SendHTTPRequest(path, &resp)
}
// Withdraw sends a withdrawal request
func (l *Lbank) Withdraw(account, assetCode, amount, memo, mark string) (WithdrawResponse, error) {
var resp WithdrawResponse
params := url.Values{}
params.Set("account", account)
params.Set("assetCode", assetCode)
params.Set("amount", amount)
if memo != "" {
params.Set("memo", memo)
}
if mark != "" {
params.Set("mark", mark)
}
path := fmt.Sprintf("%s/v%s/%s", l.API.Endpoints.URL, lbankAPIVersion, lbankWithdraw)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// RevokeWithdraw cancels the withdrawal given the withdrawalID
func (l *Lbank) RevokeWithdraw(withdrawID string) (RevokeWithdrawResponse, error) {
var resp RevokeWithdrawResponse
params := url.Values{}
if withdrawID != "" {
params.Set("withdrawId", withdrawID)
}
path := fmt.Sprintf("%s/v%s/%s?", l.API.Endpoints.URL, lbankAPIVersion, lbankRevokeWithdraw)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// GetWithdrawalRecords gets withdrawal records
func (l *Lbank) GetWithdrawalRecords(assetCode, status, pageNo, pageSize string) (WithdrawalResponse, error) {
var resp WithdrawalResponse
params := url.Values{}
params.Set("assetCode", assetCode)
params.Set("status", status)
params.Set("pageNo", pageNo)
params.Set("pageSize", pageSize)
path := fmt.Sprintf("%s/v%s/%s", l.API.Endpoints.URL, lbankAPIVersion, lbankWithdrawalRecords)
err := l.SendAuthHTTPRequest(http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
if resp.Error != 0 {
return resp, ErrorCapture(resp.Error)
}
return resp, nil
}
// ErrorCapture captures errors
func ErrorCapture(code int64) error {
msg, ok := errorCodes[code]
if !ok {
return fmt.Errorf("undefined code please check api docs for error code definition: %v", code)
}
return errors.New(msg)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (l *Lbank) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet,
path,
nil,
nil,
&result,
false,
false,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
func (l *Lbank) loadPrivKey() error {
key := strings.Join([]string{
"-----BEGIN RSA PRIVATE KEY-----",
l.API.Credentials.Secret,
"-----END RSA PRIVATE KEY-----",
}, "\n")
block, _ := pem.Decode([]byte(key))
if block == nil {
return errors.New("pem block is nil")
}
p, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return fmt.Errorf("unable to decode priv key: %s", err)
}
var ok bool
l.privateKey, ok = p.(*rsa.PrivateKey)
if !ok {
return errors.New("unable to parse RSA private key")
}
return nil
}
func (l *Lbank) sign(data string) (string, error) {
if l.privateKey == nil {
return "", errors.New("private key not loaded")
}
md5hash := gctcrypto.GetMD5([]byte(data))
m := strings.ToUpper(gctcrypto.HexEncodeToString(md5hash))
s := gctcrypto.GetSHA256([]byte(m))
r, err := rsa.SignPKCS1v15(rand.Reader, l.privateKey, crypto.SHA256, s)
if err != nil {
return "", err
}
return gctcrypto.Base64Encode(r), nil
}
// SendAuthHTTPRequest sends an authenticated request
func (l *Lbank) SendAuthHTTPRequest(method, endpoint string, vals url.Values, result interface{}) error {
if vals == nil {
vals = url.Values{}
}
vals.Set("api_key", l.API.Credentials.Key)
sig, err := l.sign(vals.Encode())
if err != nil {
return err
}
vals.Set("sign", sig)
payload := vals.Encode()
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
return l.SendPayload(method,
endpoint,
headers,
bytes.NewBufferString(payload),
&result,
true,
false,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}

View File

@@ -0,0 +1,396 @@
package lbank
import (
"fmt"
"sync"
"testing"
"time"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
// Please supply your own keys here for due diligence testing
const (
testAPIKey = ""
testAPISecret = ""
canManipulateRealOrders = false
)
var l Lbank
var setupRan bool
var m sync.Mutex
func TestSetup(t *testing.T) {
t.Parallel()
m.Lock()
defer m.Unlock()
if setupRan {
return
}
l.SetDefaults()
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json")
if err != nil {
t.Errorf("Test Failed - Lbank Setup() init error:, %v", err)
}
lbankConfig, err := cfg.GetExchangeConfig("Lbank")
if err != nil {
t.Errorf("Test Failed - Lbank Setup() init error: %v", err)
}
lbankConfig.API.AuthenticatedSupport = true
lbankConfig.API.Credentials.Secret = testAPISecret
lbankConfig.API.Credentials.Key = testAPIKey
l.Setup(lbankConfig)
setupRan = true
}
func areTestAPIKeysSet() bool {
return l.AllowAuthenticatedRequest()
}
func TestGetTicker(t *testing.T) {
TestSetup(t)
_, err := l.GetTicker("btc_usdt")
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestGetCurrencyPairs(t *testing.T) {
TestSetup(t)
_, err := l.GetCurrencyPairs()
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestGetMarketDepths(t *testing.T) {
TestSetup(t)
_, err := l.GetMarketDepths("btc_usdt", "60", "1")
if err != nil {
t.Errorf("GetMarketDepth failed: %v", err)
}
a, _ := l.GetMarketDepths("btc_usdt", "60", "0")
if len(a.Asks) != 60 {
t.Errorf("length requested doesnt match the output")
}
}
func TestGetTrades(t *testing.T) {
TestSetup(t)
_, err := l.GetTrades("btc_usdt", "600", fmt.Sprintf("%v", time.Now().Unix()))
if err != nil {
t.Errorf("test failed: %v", err)
}
a, err := l.GetTrades("btc_usdt", "600", "0")
if len(a) != 600 && err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestGetKlines(t *testing.T) {
TestSetup(t)
_, err := l.GetKlines("btc_usdt", "600", "minute1", fmt.Sprintf("%v", time.Now().Unix()))
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestUpdateOrderbook(t *testing.T) {
TestSetup(t)
p := currency.Pair{
Delimiter: "_",
Base: currency.ETH,
Quote: currency.BTC}
_, err := l.UpdateOrderbook(p.Lower(), "spot")
if err != nil {
t.Errorf("Update for orderbook failed: %v", err)
}
}
func TestGetUserInfo(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.GetUserInfo()
if err != nil {
t.Errorf("invalid key or sign: %v", err)
}
}
func TestCreateOrder(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
}
cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_")
_, err := l.CreateOrder(cp.Lower().String(), "what", 1231, 12314)
if err == nil {
t.Error("Test Failed - CreateOrder error cannot be nil")
}
_, err = l.CreateOrder(cp.Lower().String(), "buy", 0, 0)
if err == nil {
t.Error("Test Failed - CreateOrder error cannot be nil")
}
_, err = l.CreateOrder(cp.Lower().String(), "sell", 1231, 0)
if err == nil {
t.Error("Test Failed - CreateOrder error cannot be nil")
}
_, err = l.CreateOrder(cp.Lower().String(), "buy", 58, 681)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
func TestRemoveOrder(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
}
cp := currency.NewPairWithDelimiter(currency.ETH.String(), currency.BTC.String(), "_")
_, err := l.RemoveOrder(cp.Lower().String(), "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23")
if err != nil {
t.Errorf("unable to remove order: %v", err)
}
}
func TestQueryOrder(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_")
_, err := l.QueryOrder(cp.Lower().String(), "1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func TestQueryOrderHistory(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_")
_, err := l.QueryOrderHistory(cp.Lower().String(), "1", "100")
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestGetPairInfo(t *testing.T) {
TestSetup(t)
_, err := l.GetPairInfo()
if err != nil {
t.Errorf("couldnt get pair info: %v", err)
}
}
func TestOrderTransactionDetails(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.OrderTransactionDetails("eth_btc", "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23")
if err != nil {
t.Errorf("couldnt get transaction details: %v", err)
}
}
func TestTransactionHistory(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.TransactionHistory("btc_usdt", "", "", "", "", "", "")
if err != nil {
t.Errorf("couldnt get transaction history: %v", err)
}
}
func TestGetOpenOrders(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_")
_, err := l.GetOpenOrders(cp.Lower().String(), "1", "50")
if err != nil {
t.Error("unexpected error", err)
}
}
func TestUSD2RMBRate(t *testing.T) {
TestSetup(t)
_, err := l.USD2RMBRate()
if err != nil {
t.Error("unable to acquire the rate")
}
}
func TestGetWithdrawConfig(t *testing.T) {
TestSetup(t)
_, err := l.GetWithdrawConfig("eth")
if err != nil {
t.Errorf("unable to get withdraw config: %v", err)
}
}
func TestWithdraw(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
}
_, err := l.Withdraw("", "", "", "", "")
if err != nil {
t.Errorf("unable to withdraw: %v", err)
}
}
func TestGetWithdrawRecords(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.GetWithdrawalRecords("eth", "0", "1", "20")
if err != nil {
t.Errorf("unable to get withdrawal records: %v", err)
}
}
func TestLoadPrivKey(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
err := l.loadPrivKey()
if err != nil {
t.Error(err)
}
l.API.Credentials.Secret = "errortest"
err = l.loadPrivKey()
if err == nil {
t.Errorf("expected error due to pemblock nil, got err: %v", err)
}
}
func TestSign(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
l.API.Credentials.Secret = testAPISecret
l.loadPrivKey()
_, err := l.sign("hello123")
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestSubmitOrder(t *testing.T) {
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderSubmission = &exchange.OrderSubmission{
Pair: currency.Pair{
Base: currency.BTC,
Quote: currency.USDT,
Delimiter: "_",
},
OrderSide: exchange.BuyOrderSide,
OrderType: exchange.LimitOrderType,
Price: 1,
Amount: 1,
ClientID: "meowOrder",
}
response, err := l.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
func TestCancelOrder(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
}
cp := currency.NewPairWithDelimiter(currency.ETH.String(), currency.BTC.String(), "_")
var a exchange.OrderCancellation
a.CurrencyPair = cp
a.OrderID = "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23"
err := l.CancelOrder(&a)
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestGetOrderInfo(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.GetOrderInfo("9ead39f5-701a-400b-b635-d7349eb0f6b")
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestGetAllOpenOrderID(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.getAllOpenOrderID()
if err != nil {
t.Errorf("test failed: %v", err)
}
}
func TestGetFeeByType(t *testing.T) {
TestSetup(t)
cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_")
var input exchange.FeeBuilder
input.Amount = 2
input.FeeType = exchange.CryptocurrencyWithdrawalFee
input.Pair = cp
a, err := l.GetFeeByType(&input)
if err != nil {
t.Errorf("test failed. couldnt get fee: %v", err)
}
if a != 0.0005 {
t.Errorf("testGetFeeByType failed. Expected: 0.0005, Received: %v", a)
}
}
func TestGetAccountInfo(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.GetAccountInfo()
if err != nil {
t.Error(err)
}
}
func TestGetOrderHistory(t *testing.T) {
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
var input exchange.GetOrdersRequest
input.OrderSide = exchange.BuyOrderSide
_, err := l.GetOrderHistory(&input)
if err != nil {
t.Error(err)
}
}

View File

@@ -0,0 +1,270 @@
package lbank
import (
"encoding/json"
)
// Ticker stores the ticker price data for a currency pair
type Ticker struct {
Change float64 `json:"change"`
High float64 `json:"high"`
Latest float64 `json:"latest"`
Low float64 `json:"low"`
Turnover float64 `json:"turnover"`
Volume float64 `json:"vol"`
}
// TickerResponse stores the ticker price data and timestamp for a currency pair
type TickerResponse struct {
Symbol string `json:"symbol"`
Timestamp int64 `json:"timestamp"`
Ticker Ticker `json:"ticker"`
}
// MarketDepthResponse stores arrays for asks, bids and a timestamp for a currecy pair
type MarketDepthResponse struct {
ErrCapture `json:",omitempty"`
Asks [][]float64 `json:"asks"`
Bids [][]float64 `json:"bids"`
Timestamp int64 `json:"timestamp"`
}
// TradeResponse stores date_ms, amount, price, type, tid for a currency pair
type TradeResponse struct {
DateMS int64 `json:"date_ms"`
Amount float64 `json:"amount"`
Price float64 `json:"price"`
Type string `json:"type"`
TID string `json:"tid"`
}
// KlineResponse stores kline info for given currency exchange
type KlineResponse struct {
TimeStamp int64 `json:"timestamp"`
OpenPrice float64 `json:"openprice"`
HigestPrice float64 `json:"highestprice"`
LowestPrice float64 `json:"lowestprice"`
ClosePrice float64 `json:"closeprice"`
TradingVolume float64 `json:"tradingvolume"`
}
// InfoResponse stores info
type InfoResponse struct {
Freeze map[string]string `json:"freeze"`
Asset map[string]string `json:"asset"`
Free map[string]string `json:"Free"`
}
// InfoFinalResponse stores info
type InfoFinalResponse struct {
ErrCapture `json:",omitempty"`
Info InfoResponse `json:"info"`
}
// CreateOrderResponse stores the result of the Order and
type CreateOrderResponse struct {
ErrCapture `json:",omitempty"`
OrderID string `json:"order_id"`
}
// RemoveOrderResponse stores the result when an order is cancelled
type RemoveOrderResponse struct {
ErrCapture `json:",omitempty"`
Err string `json:"error"`
OrderID string `json:"order_id"`
Success string `json:"success"`
}
// OrderResponse stores the data related to the given OrderIDs
type OrderResponse struct {
Symbol string `json:"symbol"`
Amount float64 `json:"amount"`
CreateTime int64 `json:"created_time"`
Price float64 `json:"price"`
AvgPrice float64 `json:"avg_price"`
Type string `json:"type"`
OrderID string `json:"order_id"`
DealAmount float64 `json:"deal_amount"`
Status int64 `json:"status"`
}
// QueryOrderResponse stores the data from queries
type QueryOrderResponse struct {
ErrCapture `json:",omitempty"`
Orders json.RawMessage `json:"orders"`
}
// QueryOrderFinalResponse stores data from queries
type QueryOrderFinalResponse struct {
ErrCapture
Orders []OrderResponse
}
// OrderHistory stores data for past orders
type OrderHistory struct {
Result bool `json:"result,string"`
Total string `json:"total"`
PageLength uint8 `json:"page_length"`
Orders json.RawMessage `json:"orders"`
CurrentPage uint8 `json:"current_page"`
ErrorCode int64 `json:"error_code"`
}
// OrderHistoryResponse stores past orders
type OrderHistoryResponse struct {
ErrCapture `json:",omitempty"`
PageLength uint8 `json:"page_length"`
Orders json.RawMessage `json:"orders"`
CurrentPage uint8 `json:"current_page"`
}
// OrderHistoryFinalResponse stores past orders
type OrderHistoryFinalResponse struct {
ErrCapture
PageLength uint8
Orders []OrderResponse
CurrentPage uint8
}
// PairInfoResponse stores information about trading pairs
type PairInfoResponse struct {
MinimumQuantity string `json:"minTranQua"`
PriceAccuracy string `json:"priceAccuracy"`
QuantityAccuracy string `json:"quantityAccuracy"`
Symbol string `json:"symbol"`
}
// TransactionTemp stores details about transactions
type TransactionTemp struct {
TxUUID string `json:"txUuid"`
OrderUUID string `json:"orderUuid"`
TradeType string `json:"tradeType"`
DealTime int64 `json:"dealTime"`
DealPrice float64 `json:"dealPrice"`
DealQuantity float64 `json:"dealQuantity"`
DealVolPrice float64 `json:"dealVolumePrice"`
TradeFee float64 `json:"tradeFee"`
TradeFeeRate float64 `json:"tradeFeeRate"`
}
// TransactionHistoryResp stores details about past transactions
type TransactionHistoryResp struct {
ErrCapture `json:",omitempty"`
Transaction []TransactionTemp `json:"transaction"`
}
// OpenOrderResponse stores information about the opening orders
type OpenOrderResponse struct {
ErrCapture `json:",omitempty"`
PageLength uint8 `json:"page_length"`
PageNumber uint8 `json:"page_number"`
Total string `json:"total"`
Orders json.RawMessage `json:"orders"`
}
// OpenOrderFinalResponse stores the unmarshalled value of OpenOrderResponse
type OpenOrderFinalResponse struct {
ErrCapture
PageLength uint8
PageNumber uint8
Total string
Orders []OrderResponse
}
// ExchangeRateResponse stores information about USD-RMB rate
type ExchangeRateResponse struct {
USD2CNY string `json:"USD2CNY"`
}
// WithdrawConfigResponse stores info about withdrawal configurations
type WithdrawConfigResponse struct {
AssetCode string `json:"assetCode"`
Minimum string `json:"min"`
CanWithDraw bool `json:"canWithDraw"`
Fee string `json:"fee"`
}
// WithdrawResponse stores info about the withdrawal
type WithdrawResponse struct {
ErrCapture `json:",omitempty"`
WithdrawID string `json:"withdrawId"`
Fee float64 `json:"fee"`
}
// RevokeWithdrawResponse stores info about the revoked withdrawal
type RevokeWithdrawResponse struct {
ErrCapture `json:",omitempty"`
WithdrawID string `json:"string"`
}
// ListDataResponse contains some of withdrawal data
type ListDataResponse struct {
ErrCapture `json:",omitempty"`
Amount float64 `json:"amount"`
AssetCode string `json:"assetCode"`
Address string `json:"address"`
Fee float64 `json:"fee"`
ID int64 `json:"id"`
Time int64 `json:"time"`
TXHash string `json:"txhash"`
Status string `json:"status"`
}
// WithdrawalResponse stores data for withdrawals
type WithdrawalResponse struct {
ErrCapture `json:",omitempty"`
TotalPages int64 `json:"totalPages"`
PageSize int64 `json:"pageSize"`
PageNo int64 `json:"pageNo"`
List []ListDataResponse `json:"list"`
}
// ErrCapture helps with error info
type ErrCapture struct {
Error int64 `json:"error_code"`
Result bool `json:"result,string"`
}
// GetAllOpenIDResp stores orderIds and currency pairs for open orders
type GetAllOpenIDResp struct {
CurrencyPair string
OrderID string
}
var errorCodes = map[int64]string{
10000: "Internal error",
10001: "The required parameters can not be empty",
10002: "Validation Failed",
10003: "Invalid parameter",
10004: "Request too frequent",
10005: "Secret key does not exist",
10006: "User does not exist",
10007: "Invalid signature",
10008: "Invalid Trading Pair",
10009: "Price and/or Amount are required for limit order",
10010: "Price and/or Amount must be more than 0",
10013: "The amount is too small",
10014: "Insufficient amount of money in account",
10015: "Invalid order type",
10016: "Insufficient account balance",
10017: "Server Error",
10018: "Page size should be between 1 and 50",
10019: "Cancel NO more than 3 orders in one request",
10020: "Volume < 0.001",
10021: "Price < 0.01",
10022: "Access denied",
10023: "Market Order is not supported yet.",
10024: "User cannot trade on this pair",
10025: "Order has been filled",
10026: "Order has been cancelld",
10027: "Order is cancelling",
10028: "Wrong query time",
10029: "'from' is not in the query time",
10030: "'from' does not match the transaction type of inqury",
10100: "Has no privilege to withdraw",
10101: "Invalid fee rate to withdraw",
10102: "Too little to withdraw",
10103: "Exceed daily limitation of withdraw",
10104: "Cancel was rejected",
10105: "Request has been cancelled",
}

View File

@@ -0,0 +1,668 @@
package lbank
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"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/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// GetDefaultConfig returns a default exchange config
func (l *Lbank) GetDefaultConfig() (*config.ExchangeConfig, error) {
l.SetDefaults()
exchCfg := new(config.ExchangeConfig)
exchCfg.Name = l.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = l.BaseCurrencies
err := l.SetupDefaults(exchCfg)
if err != nil {
return nil, err
}
if l.Features.Supports.RESTCapabilities.AutoPairUpdates {
err = l.UpdateTradablePairs(true)
if err != nil {
return nil, err
}
}
return exchCfg, nil
}
// SetDefaults sets the basic defaults for Lbank
func (l *Lbank) SetDefaults() {
l.Name = "Lbank"
l.Enabled = true
l.Verbose = true
l.API.CredentialsValidator.RequiresKey = true
l.API.CredentialsValidator.RequiresSecret = true
l.CurrencyPairs = currency.PairsManager{
AssetTypes: asset.Items{
asset.Spot,
},
UseGlobalFormat: true,
RequestFormat: &currency.PairFormat{
Delimiter: "_",
},
ConfigFormat: &currency.PairFormat{
Delimiter: "_",
},
}
l.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
RESTCapabilities: exchange.ProtocolFeatures{
AutoPairUpdates: true,
},
WithdrawPermissions: exchange.AutoWithdrawCryptoWithAPIPermission |
exchange.NoFiatWithdrawals,
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: true,
},
}
l.Requester = request.New(l.Name,
request.NewRateLimit(time.Second, lbankAuthRateLimit),
request.NewRateLimit(time.Second, lbankUnAuthRateLimit),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
l.API.Endpoints.URLDefault = lbankAPIURL
l.API.Endpoints.URL = l.API.Endpoints.URLDefault
}
// Setup sets exchange configuration profile
func (l *Lbank) Setup(exch *config.ExchangeConfig) error {
if !exch.Enabled {
l.SetEnabled(false)
return nil
}
err := l.SetupDefaults(exch)
if err != nil {
return err
}
if l.API.AuthenticatedSupport {
err = l.loadPrivKey()
if err != nil {
l.API.AuthenticatedSupport = false
log.Errorf(log.ExchangeSys, "%s couldn't load private key, setting authenticated support to false", l.Name)
}
}
return nil
}
// Start starts the LakeBTC go routine
func (l *Lbank) Start(wg *sync.WaitGroup) {
wg.Add(1)
go func() {
l.Run()
wg.Done()
}()
}
// Run implements the Lbank wrapper
func (l *Lbank) Run() {
if l.Verbose {
l.PrintEnabledPairs()
}
if !l.GetEnabledFeatures().AutoPairUpdates {
return
}
err := l.UpdateTradablePairs(false)
if err != nil {
log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", l.Name, err)
}
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (l *Lbank) FetchTradablePairs(asset asset.Item) ([]string, error) {
currencies, err := l.GetCurrencyPairs()
if err != nil {
return nil, err
}
return currencies, nil
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (l *Lbank) UpdateTradablePairs(forceUpdate bool) error {
pairs, err := l.FetchTradablePairs(asset.Spot)
if err != nil {
return err
}
return l.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate)
}
// UpdateTicker updates and returns the ticker for a currency pair
func (l *Lbank) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
var tickerPrice ticker.Price
tickerInfo, err := l.GetTicker(l.FormatExchangeCurrency(p, assetType).String())
if err != nil {
return tickerPrice, err
}
tickerPrice.Pair = p
tickerPrice.Last = tickerInfo.Ticker.Latest
tickerPrice.High = tickerInfo.Ticker.High
tickerPrice.Volume = tickerInfo.Ticker.Volume
tickerPrice.Low = tickerInfo.Ticker.Low
err = ticker.ProcessTicker(l.GetName(), &tickerPrice, assetType)
if err != nil {
return tickerPrice, err
}
return ticker.GetTicker(l.Name, p, assetType)
}
// FetchTicker returns the ticker for a currency pair
func (l *Lbank) FetchTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
tickerNew, err := ticker.GetTicker(l.GetName(),
l.FormatExchangeCurrency(p, assetType), assetType)
if err != nil {
return l.UpdateTicker(p, assetType)
}
return tickerNew, nil
}
// FetchOrderbook returns orderbook base on the currency pair
func (l *Lbank) FetchOrderbook(currency currency.Pair, assetType asset.Item) (orderbook.Base, error) {
ob, err := orderbook.Get(l.GetName(), currency, assetType)
if err != nil {
return l.UpdateOrderbook(currency, assetType)
}
return ob, nil
}
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (l *Lbank) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
a, err := l.GetMarketDepths(l.FormatExchangeCurrency(p, assetType).String(), "60", "1")
if err != nil {
return orderBook, err
}
for i := range a.Asks {
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
Price: a.Asks[i][0],
Amount: a.Asks[i][1]})
}
for i := range a.Bids {
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
Price: a.Bids[i][0],
Amount: a.Bids[i][1]})
}
orderBook.Pair = p
orderBook.ExchangeName = l.GetName()
orderBook.AssetType = assetType
err = orderBook.Process()
if err != nil {
return orderBook, err
}
return orderbook.Get(l.Name, p, assetType)
}
// GetAccountInfo retrieves balances for all enabled currencies for the
// Lbank exchange
func (l *Lbank) GetAccountInfo() (exchange.AccountInfo, error) {
var info exchange.AccountInfo
data, err := l.GetUserInfo()
if err != nil {
return info, err
}
var account exchange.Account
for key, val := range data.Info.Asset {
c := currency.NewCode(key)
hold, ok := data.Info.Freeze[key]
if !ok {
return info, fmt.Errorf("hold data not found with %s", key)
}
totalVal, err := strconv.ParseFloat(val, 64)
if err != nil {
return info, err
}
totalHold, err := strconv.ParseFloat(hold, 64)
if err != nil {
return info, err
}
account.Currencies = append(account.Currencies,
exchange.AccountCurrencyInfo{CurrencyName: c,
TotalValue: totalVal,
Hold: totalHold})
}
info.Accounts = append(info.Accounts, account)
info.Exchange = l.GetName()
return info, nil
}
// GetFundingHistory returns funding history, deposits and
// withdrawals
func (l *Lbank) GetFundingHistory() ([]exchange.FundHistory, error) {
return nil, common.ErrFunctionNotSupported
}
// GetExchangeHistory returns historic trade data since exchange opening.
func (l *Lbank) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
return nil, common.ErrFunctionNotSupported
}
// SubmitOrder submits a new order
func (l *Lbank) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
var resp exchange.SubmitOrderResponse
if order == nil {
return resp, exchange.ErrOrderSubmissionIsNil
}
if err := order.Validate(); err != nil {
return resp, err
}
if order.OrderSide != exchange.BuyOrderSide &&
order.OrderSide != exchange.SellOrderSide {
return resp,
fmt.Errorf("%s order side is not supported by the exchange",
order.OrderSide)
}
tempResp, err := l.CreateOrder(
l.FormatExchangeCurrency(order.Pair, asset.Spot).String(),
order.OrderSide.ToString(),
order.Amount,
order.Price)
if err != nil {
return resp, err
}
resp.IsOrderPlaced = true
resp.OrderID = tempResp.OrderID
return resp, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (l *Lbank) ModifyOrder(action *exchange.ModifyOrder) (string, error) {
return "", common.ErrFunctionNotSupported
}
// CancelOrder cancels an order by its corresponding ID number
func (l *Lbank) CancelOrder(order *exchange.OrderCancellation) error {
_, err := l.RemoveOrder(l.FormatExchangeCurrency(order.CurrencyPair,
order.AssetType).String(), order.OrderID)
return err
}
// CancelAllOrders cancels all orders associated with a currency pair
func (l *Lbank) CancelAllOrders(orders *exchange.OrderCancellation) (exchange.CancelAllOrdersResponse, error) {
var resp exchange.CancelAllOrdersResponse
orderIDs, err := l.getAllOpenOrderID()
if err != nil {
return resp, nil
}
for key := range orderIDs {
if key != orders.CurrencyPair.String() {
continue
}
var x, y = 0, 0
var input string
var tempSlice []string
for x <= len(orderIDs[key]) {
x++
for y != x {
tempSlice = append(tempSlice, orderIDs[key][y])
if y%3 == 0 {
input = strings.Join(tempSlice, ",")
CancelResponse, err2 := l.RemoveOrder(key, input)
if err2 != nil {
return resp, err2
}
tempStringSuccess := strings.Split(CancelResponse.Success, ",")
for k := range tempStringSuccess {
resp.OrderStatus[tempStringSuccess[k]] = "Cancelled"
}
tempStringError := strings.Split(CancelResponse.Err, ",")
for l := range tempStringError {
resp.OrderStatus[tempStringError[l]] = "Failed"
}
tempSlice = tempSlice[:0]
y++
}
y++
}
input = strings.Join(tempSlice, ",")
CancelResponse, err2 := l.RemoveOrder(key, input)
if err2 != nil {
return resp, err2
}
tempStringSuccess := strings.Split(CancelResponse.Success, ",")
for k := range tempStringSuccess {
resp.OrderStatus[tempStringSuccess[k]] = "Cancelled"
}
tempStringError := strings.Split(CancelResponse.Err, ",")
for l := range tempStringError {
resp.OrderStatus[tempStringError[l]] = "Failed"
}
tempSlice = tempSlice[:0]
}
}
return resp, nil
}
// GetOrderInfo returns information on a current open order
func (l *Lbank) GetOrderInfo(orderID string) (exchange.OrderDetail, error) {
var resp exchange.OrderDetail
orderIDs, err := l.getAllOpenOrderID()
if err != nil {
return resp, err
}
for key, val := range orderIDs {
for i := range val {
if val[i] != orderID {
continue
}
tempResp, err := l.QueryOrder(key, orderID)
if err != nil {
return resp, err
}
resp.Exchange = l.GetName()
resp.CurrencyPair = currency.NewPairFromString(key)
if strings.EqualFold(tempResp.Orders[0].Type, "buy") {
resp.OrderSide = exchange.BuyOrderSide
} else {
resp.OrderSide = exchange.SellOrderSide
}
z := tempResp.Orders[0].Status
switch {
case z == -1:
resp.Status = "cancelled"
case z == 0:
resp.Status = "on trading"
case z == 1:
resp.Status = "filled partially"
case z == 2:
resp.Status = "Filled totally"
case z == 4:
resp.Status = "Cancelling"
default:
resp.Status = "Invalid Order Status"
}
resp.Price = tempResp.Orders[0].Price
resp.Amount = tempResp.Orders[0].Amount
resp.ExecutedAmount = tempResp.Orders[0].DealAmount
resp.RemainingAmount = tempResp.Orders[0].Amount - tempResp.Orders[0].DealAmount
resp.Fee, err = l.GetFeeByType(&exchange.FeeBuilder{
FeeType: exchange.CryptocurrencyTradeFee,
Amount: tempResp.Orders[0].Amount,
PurchasePrice: tempResp.Orders[0].Price})
if err != nil {
resp.Fee = lbankFeeNotFound
}
}
}
return resp, nil
}
// GetDepositAddress returns a deposit address for a specified currency
func (l *Lbank) GetDepositAddress(cryptocurrency currency.Code, accountID string) (string, error) {
return "", common.ErrFunctionNotSupported
}
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (l *Lbank) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) {
resp, err := l.Withdraw(withdrawRequest.Address, withdrawRequest.Currency.String(), strconv.FormatFloat(withdrawRequest.Amount, 'f', -1, 64), "", withdrawRequest.Description)
return resp.WithdrawID, err
}
// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is
// submitted
func (l *Lbank) WithdrawFiatFunds(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrFunctionNotSupported
}
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a withdrawal is
// submitted
func (l *Lbank) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchange.FiatWithdrawRequest) (string, error) {
return "", common.ErrFunctionNotSupported
}
// GetWebsocket returns a pointer to the exchange websocket
func (l *Lbank) GetWebsocket() (*wshandler.Websocket, error) {
return nil, common.ErrNotYetImplemented
}
// GetActiveOrders retrieves any orders that are active/open
func (l *Lbank) GetActiveOrders(getOrdersRequest *exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
var finalResp []exchange.OrderDetail
var resp exchange.OrderDetail
tempData, err := l.getAllOpenOrderID()
if err != nil {
return finalResp, err
}
for key, val := range tempData {
for x := range val {
tempResp, err := l.QueryOrder(key, val[x])
if err != nil {
return finalResp, err
}
resp.Exchange = l.GetName()
resp.CurrencyPair = currency.NewPairFromString(key)
if strings.EqualFold(tempResp.Orders[0].Type, "buy") {
resp.OrderSide = exchange.BuyOrderSide
} else {
resp.OrderSide = exchange.SellOrderSide
}
z := tempResp.Orders[0].Status
switch {
case z == -1:
resp.Status = "cancelled"
case z == 1:
resp.Status = "on trading"
case z == 2:
resp.Status = "filled partially"
case z == 3:
resp.Status = "Filled totally"
case z == 4:
resp.Status = "Cancelling"
default:
resp.Status = "Invalid Order Status"
}
resp.Price = tempResp.Orders[0].Price
resp.Amount = tempResp.Orders[0].Amount
resp.OrderDate = time.Unix(tempResp.Orders[0].CreateTime, 9)
resp.ExecutedAmount = tempResp.Orders[0].DealAmount
resp.RemainingAmount = tempResp.Orders[0].Amount - tempResp.Orders[0].DealAmount
resp.Fee, err = l.GetFeeByType(&exchange.FeeBuilder{
FeeType: exchange.CryptocurrencyTradeFee,
Amount: tempResp.Orders[0].Amount,
PurchasePrice: tempResp.Orders[0].Price})
if err != nil {
resp.Fee = lbankFeeNotFound
}
for y := int(0); y < len(getOrdersRequest.Currencies); y++ {
if getOrdersRequest.Currencies[y].String() != key {
continue
}
if getOrdersRequest.OrderSide == "ANY" {
finalResp = append(finalResp, resp)
continue
}
if strings.EqualFold(getOrdersRequest.OrderSide.ToString(), tempResp.Orders[0].Type) {
finalResp = append(finalResp, resp)
}
}
}
}
return finalResp, nil
}
// GetOrderHistory retrieves account order information *
// Can Limit response to specific order status
func (l *Lbank) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest) ([]exchange.OrderDetail, error) {
var finalResp []exchange.OrderDetail
var resp exchange.OrderDetail
var tempCurr currency.Pairs
var x int
if len(getOrdersRequest.Currencies) == 0 {
tempCurr = l.GetEnabledPairs(asset.Spot)
} else {
for x < len(getOrdersRequest.Currencies) {
tempCurr = getOrdersRequest.Currencies
}
}
for a := range tempCurr {
p := l.FormatExchangeCurrency(tempCurr[a], asset.Spot).String()
b := int64(1)
tempResp, err := l.QueryOrderHistory(p, strconv.FormatInt(b, 10), "200")
if err != nil {
return finalResp, err
}
for len(tempResp.Orders) != 0 {
tempResp, err = l.QueryOrderHistory(p, strconv.FormatInt(b, 10), "200")
if err != nil {
return finalResp, err
}
for x := 0; x < len(tempResp.Orders); x++ {
resp.Exchange = l.GetName()
resp.CurrencyPair = currency.NewPairFromString(tempResp.Orders[x].Symbol)
if strings.EqualFold(tempResp.Orders[x].Type, "buy") {
resp.OrderSide = exchange.BuyOrderSide
} else {
resp.OrderSide = exchange.SellOrderSide
}
z := tempResp.Orders[x].Status
switch {
case z == -1:
resp.Status = "cancelled"
case z == 1:
resp.Status = "on trading"
case z == 2:
resp.Status = "filled partially"
case z == 3:
resp.Status = "Filled totally"
case z == 4:
resp.Status = "Cancelling"
default:
resp.Status = "Invalid Order Status"
}
resp.Price = tempResp.Orders[x].Price
resp.Amount = tempResp.Orders[x].Amount
resp.OrderDate = time.Unix(tempResp.Orders[x].CreateTime, 9)
resp.ExecutedAmount = tempResp.Orders[x].DealAmount
resp.RemainingAmount = tempResp.Orders[x].Price - tempResp.Orders[x].DealAmount
resp.Fee, err = l.GetFeeByType(&exchange.FeeBuilder{
FeeType: exchange.CryptocurrencyTradeFee,
Amount: tempResp.Orders[x].Amount,
PurchasePrice: tempResp.Orders[x].Price})
if err != nil {
resp.Fee = lbankFeeNotFound
}
finalResp = append(finalResp, resp)
b++
}
}
}
return finalResp, nil
}
// GetFeeByType returns an estimate of fee based on the type of transaction *
func (l *Lbank) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
var resp float64
if feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
return feeBuilder.Amount * feeBuilder.PurchasePrice * 0.002, nil
}
if feeBuilder.FeeType == exchange.CryptocurrencyWithdrawalFee {
withdrawalFee, err := l.GetWithdrawConfig(feeBuilder.Pair.Base.Lower().String())
if err != nil {
return resp, err
}
var tempFee string
temp := strings.Split(withdrawalFee[0].Fee, ":\"")
if len(temp) > 1 {
tempFee = strings.TrimRight(temp[1], ",\"type")
} else {
tempFee = temp[0]
}
resp, err = strconv.ParseFloat(tempFee, 64)
if err != nil {
return resp, err
}
}
return resp, nil
}
// GetAllOpenOrderID returns all open orders by currency pairs
func (l *Lbank) getAllOpenOrderID() (map[string][]string, error) {
allPairs := l.GetEnabledPairs(asset.Spot)
resp := make(map[string][]string)
for a := range allPairs {
p := l.FormatExchangeCurrency(allPairs[a], asset.Spot).String()
b := int64(1)
tempResp, err := l.GetOpenOrders(p, strconv.FormatInt(b, 10), "200")
if err != nil {
return resp, err
}
tempData := len(tempResp.Orders)
for tempData != 0 {
tempResp, err = l.GetOpenOrders(p, strconv.FormatInt(b, 10), "200")
if err != nil {
return resp, err
}
if len(tempResp.Orders) == 0 {
return resp, nil
}
for c := 0; c < tempData; c++ {
resp[p] = append(resp[p], tempResp.Orders[c].OrderID)
}
tempData = len(tempResp.Orders)
b++
}
}
return resp, nil
}
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle subscribing
func (l *Lbank) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
return common.ErrNotYetImplemented
}
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
// which lets websocket.manageSubscriptions handle unsubscribing
func (l *Lbank) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
return common.ErrNotYetImplemented
}
// AuthenticateWebsocket authenticates it
func (l *Lbank) AuthenticateWebsocket() error {
return common.ErrNotYetImplemented
}
// GetSubscriptions gets subscriptions
func (l *Lbank) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
return nil, common.ErrNotYetImplemented
}

View File

@@ -1,6 +1,7 @@
package localbitcoins
import (
"bytes"
"errors"
"fmt"
"net/http"
@@ -142,16 +143,36 @@ func (l *LocalBitcoins) GetAccountInformation(username string, self bool) (Accou
// adID - [optional] string if omitted returns all ads
func (l *LocalBitcoins) Getads(args ...string) (AdData, error) {
var resp struct {
Data AdData `json:"data"`
Data AdData `json:"data"`
Error struct {
Message string `json:"message"`
Code int `json:"error_code"`
} `json:"error"`
}
var err error
if len(args) == 0 {
return resp.Data, l.SendAuthenticatedHTTPRequest(http.MethodGet, localbitcoinsAPIAds, nil, &resp)
err = l.SendAuthenticatedHTTPRequest(http.MethodGet,
localbitcoinsAPIAds,
nil,
&resp)
} else {
params := url.Values{"ads": {strings.Join(args, ",")}}
err = l.SendAuthenticatedHTTPRequest(http.MethodGet,
localbitcoinsAPIAdGet,
params,
&resp)
}
params := url.Values{"ads": {strings.Join(args, ",")}}
if err != nil {
return resp.Data, err
}
return resp.Data, l.SendAuthenticatedHTTPRequest(http.MethodGet, localbitcoinsAPIAdGet, params, &resp)
if resp.Error.Message != "" {
return resp.Data, errors.New(resp.Error.Message)
}
return resp.Data, nil
}
// EditAd updates set advertisements
@@ -160,12 +181,27 @@ func (l *LocalBitcoins) Getads(args ...string) (AdData, error) {
// adID - string for the ad you already created
// TODO
func (l *LocalBitcoins) EditAd(_ *AdEdit, adID string) error {
type response struct {
Data AdData `json:"data"`
resp := struct {
Data AdData `json:"data"`
Error struct {
Message string `json:"message"`
Code int `json:"error_code"`
}
}{}
err := l.SendAuthenticatedHTTPRequest(http.MethodPost,
localbitcoinsAPIAdEdit+adID+"/",
nil,
&resp)
if err != nil {
return err
}
resp := response{}
return l.SendAuthenticatedHTTPRequest(http.MethodPost, localbitcoinsAPIAdEdit+adID+"/", nil, &resp)
if resp.Error.Message != "" {
return errors.New(resp.Error.Message)
}
return nil
}
// CreateAd creates a new advertisement
@@ -192,7 +228,26 @@ func (l *LocalBitcoins) UpdatePriceEquation(adID string) error {
// adID - string of specific ad identification
// TODO
func (l *LocalBitcoins) DeleteAd(adID string) error {
return l.SendAuthenticatedHTTPRequest(http.MethodPost, localbitcoinsAPIDeleteAd+adID, nil, nil)
resp := struct {
Error struct {
Message string `json:"message"`
Code int `json:"error_code"`
} `json:"error"`
}{}
err := l.SendAuthenticatedHTTPRequest(http.MethodPost,
localbitcoinsAPIDeleteAd+adID+"/",
nil,
&resp)
if err != nil {
return err
}
if resp.Error.Message != "" {
return errors.New(resp.Error.Message)
}
return nil
}
// ReleaseFunds releases Bitcoin trades specified by ID {contact_id}. If the
@@ -517,7 +572,7 @@ func (l *LocalBitcoins) GetWalletBalance() (WalletBalanceInfo, error) {
// On success, the response returns a message indicating success. It is highly
// recommended to minimize the lifetime of access tokens with the money
// permission. Use Logout() to make the current token expire instantly.
func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) (bool, error) {
func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) error {
values := url.Values{}
values.Set("address", address)
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
@@ -528,23 +583,34 @@ func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) (b
path = localbitcoinsAPIWalletSendPin
}
type response struct {
resp := struct {
Error struct {
Message string `json:"message"`
Errors map[string]string `json:"errors"`
Code int `json:"error_code"`
} `json:"error"`
Data struct {
Message string `json:"message"`
} `json:"data"`
}
}{}
resp := response{}
err := l.SendAuthenticatedHTTPRequest(http.MethodPost, path, values, &resp)
if err != nil {
return false, err
return err
}
if resp.Data.Message != "Money is being sent" {
return false, errors.New("unable to send Bitcoins")
if len(resp.Error.Errors) != 0 {
var details string
for _, val := range resp.Error.Errors {
details += val
}
return errors.New(details)
}
return errors.New(resp.Data.Message)
}
return true, nil
return nil
}
// GetWalletAddress returns an unused receiving address from the token owner's
@@ -665,7 +731,16 @@ func (l *LocalBitcoins) GetOrderbook(currency string) (Orderbook, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (l *LocalBitcoins) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, false, l.Verbose, l.HTTPDebugging)
return l.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to
@@ -695,8 +770,16 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, params
path += "?" + encoded
}
return l.SendPayload(method, l.API.Endpoints.URL+path, headers,
strings.NewReader(encoded), result, true, true, l.Verbose, l.HTTPDebugging)
return l.SendPayload(method,
l.API.Endpoints.URL+path,
headers,
bytes.NewBufferString(encoded),
result,
true,
true,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -0,0 +1,32 @@
//+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 localbitcoins
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()
cfg.LoadConfig("../../testdata/configtest.json")
localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins")
if err != nil {
log.Fatal("Test Failed - LocalBitcoins Setup() init error", err)
}
localbitcoinsConfig.API.AuthenticatedSupport = true
localbitcoinsConfig.API.Credentials.Key = apiKey
localbitcoinsConfig.API.Credentials.Secret = apiSecret
l.SetDefaults()
l.Setup(localbitcoinsConfig)
log.Printf(sharedtestvalues.LiveTesting, l.GetName(), l.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,45 @@
//+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 localbitcoins
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/localbitcoins/localbitcoins.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins")
if err != nil {
log.Fatal("Test Failed - Localbitcoins Setup() init error", err)
}
l.SkipAuthCheck = true
localbitcoinsConfig.API.AuthenticatedSupport = true
localbitcoinsConfig.API.Credentials.Key = apiKey
localbitcoinsConfig.API.Credentials.Secret = apiSecret
l.SetDefaults()
l.Setup(localbitcoinsConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
l.HTTPClient = newClient
l.API.Endpoints.URL = serverDetails
log.Printf(sharedtestvalues.MockTesting, l.GetName(), l.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -4,13 +4,10 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
var l LocalBitcoins
// Please supply your own APIKEYS here for due diligence testing
const (
@@ -19,26 +16,11 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
l.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins")
if err != nil {
t.Error("Test Failed - LakeBTC Setup() init error")
}
localbitcoinsConfig.API.AuthenticatedSupport = true
localbitcoinsConfig.API.Credentials.Key = apiKey
localbitcoinsConfig.API.Credentials.Secret = apiSecret
l.Setup(localbitcoinsConfig)
}
var l LocalBitcoins
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := l.GetTicker()
if err != nil {
t.Errorf("Test failed - GetTicker() returned: %s", err)
@@ -47,6 +29,7 @@ func TestGetTicker(t *testing.T) {
func TestGetTradableCurrencies(t *testing.T) {
t.Parallel()
_, err := l.GetTradableCurrencies()
if err != nil {
t.Errorf("Test failed - GetTradableCurrencies() returned: %s", err)
@@ -55,43 +38,42 @@ func TestGetTradableCurrencies(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
if !l.ValidateAPICredentials() {
t.Skip()
}
_, err := l.GetAccountInformation("", true)
if err == nil {
t.Error("Test Failed - GetAccountInformation() error", err)
}
_, err = l.GetAccountInformation("bitcoinbaron", false)
if err != nil {
t.Error("Test Failed - GetAccountInformation() error", err)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get AccountInformation: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get AccountInformation: %s", err)
}
}
func TestGetads(t *testing.T) {
t.Parallel()
if !l.ValidateAPICredentials() {
t.Skip()
}
_, err := l.Getads("")
if err == nil {
t.Error("Test Failed - Getads() - Full list, error", err)
}
_, err = l.Getads("1337")
if err == nil {
t.Error("Test Failed - Getads() error", err)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get ads: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting error until QA pass")
}
}
func TestEditAd(t *testing.T) {
t.Parallel()
if !l.ValidateAPICredentials() {
t.Skip()
}
edit := AdEdit{}
var edit AdEdit
err := l.EditAd(&edit, "1337")
if err == nil {
t.Error("Test Failed - EditAd() error", err)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not edit order: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting error until QA pass")
}
}
@@ -110,6 +92,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
l.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
@@ -124,7 +107,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
l.SetDefaults()
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil {
@@ -200,45 +183,53 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
l.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText +
" & " +
exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := l.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := l.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get active orders: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get active orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := l.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get order history: %s", err)
}
}
@@ -249,10 +240,9 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -268,62 +258,62 @@ func TestSubmitOrder(t *testing.T) {
ClientID: "meowOrder",
}
response, err := l.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests:
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting an error until QA pass")
}
}
func TestCancelExchangeOrder(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
err := l.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass")
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
resp, err := l.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Errorf("Could not cancel orders: %v", err)
}
@@ -333,15 +323,17 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := l.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
if err != common.ErrFunctionNotSupported {
t.Error("Test failed - ModifyOrder() error", err)
}
}
func TestWithdraw(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -351,59 +343,55 @@ func TestWithdraw(t *testing.T) {
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := l.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass")
}
}
func TestWithdrawFiat(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := l.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
_, err := l.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestGetDepositAddress(t *testing.T) {
if apiKey != "" || apiSecret != "" {
_, err := l.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress() error", err)
}
} else {
_, err := l.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
t.Parallel()
_, err := l.GetDepositAddress(currency.BTC, "")
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetDepositAddress() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetDepositAddress() expecting an error when no APIKeys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetDepositAddress() error", err)
}
}

View File

@@ -380,8 +380,10 @@ func (l *LocalBitcoins) GetDepositAddress(cryptocurrency currency.Code, _ string
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (l *LocalBitcoins) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) {
_, err := l.WalletSend(withdrawRequest.Address, withdrawRequest.Amount, withdrawRequest.PIN)
return "", err
return "",
l.WalletSend(withdrawRequest.Address,
withdrawRequest.Amount,
withdrawRequest.PIN)
}
// WithdrawFiatFunds returns a withdrawal ID when a
@@ -403,7 +405,7 @@ func (l *LocalBitcoins) GetWebsocket() (*wshandler.Websocket, error) {
// GetFeeByType returns an estimate of fee based on type of transaction
func (l *LocalBitcoins) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !l.AllowAuthenticatedRequest() && // Todo check connection status
if (!l.AllowAuthenticatedRequest() || l.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}

174
exchanges/mock/README.md Normal file
View File

@@ -0,0 +1,174 @@
# GoCryptoTrader package Mock
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://travis-ci.org/thrasher-corp/gocryptotrader.svg?branch=master)](https://travis-ci.org/thrasher-corp/gocryptotrader)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/mock)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This Mock package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTQyYjIxNGVhMWU5MDZlOGYzMmE0NTJmM2MzYWY5NGMzMmM4MzUwNTBjZTEzNjIwODM5NDcxODQwZDljMGQyNGY)
## Mock Testing Suite
### Current Features
+ REST recording service
+ REST mock response server
### How to enable
+ Mock testing is enabled by default in some exchanges; to disable and run live endpoint testing parse -tags=mock_test_off as a go test param.
+ To record a live endpoint create two files for an exchange.
### file one - your_current_exchange_name_live_test.go
```go
//+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 your_current_exchange_name
import (
"os"
"testing"
"log"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
your_current_exchange_nameConfig, err := cfg.GetExchangeConfig("your_current_exchange_name")
if err != nil {
log.Fatal("Test Failed - your_current_exchange_name Setup() init error", err)
}
your_current_exchange_nameConfig.AuthenticatedAPISupport = true
your_current_exchange_nameConfig.APIKey = apiKey
your_current_exchange_nameConfig.APISecret = apiSecret
l.SetDefaults()
l.Setup(&your_current_exchange_nameConfig)
log.Printf(sharedtestvalues.LiveTesting, l.GetName(), l.APIUrl)
os.Exit(m.Run())
}
```
### file two - your_current_exchange_name_mock_test.go
```go
//+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 your_current_exchange_name
import (
"os"
"testing"
"log"
"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/your_current_exchange_name/your_current_exchange_name.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
your_current_exchange_nameConfig, err := cfg.GetExchangeConfig("your_current_exchange_name")
if err != nil {
log.Fatal("Test Failed - your_current_exchange_name Setup() init error", err)
}
your_current_exchange_nameConfig.AuthenticatedAPISupport = true
your_current_exchange_nameConfig.APIKey = apiKey
your_current_exchange_nameConfig.APISecret = apiSecret
l.SetDefaults()
l.Setup(&your_current_exchange_nameConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
g.HTTPClient = newClient
g.APIUrl = serverDetails
log.Printf(sharedtestvalues.MockTesting, l.GetName(), l.APIUrl)
os.Exit(m.Run())
}
```
+ Once those files are completed go through each invidual test function and add
```go
var s SomeExchange
func TestDummyTest(t *testing.T) {
s.APIURL = exchangeDefaultURL // This will overwrite the current mock url at localhost
s.Verbose = true // This will show you some fancy debug output
s.HTTPRecording = true // This will record the request and response payloads
err := s.SomeExchangeEndpointFunction()
// check error
}
```
+ After this is completed it should populate a new mocktest.json file for you with the relavent payloads in testdata
+ To check if the recording was successful, comment out recording and apiurl changes, then re-run test.
```go
var s SomeExchange
func TestDummyTest(t *testing.T) {
// s.APIURL = exchangeDefaultURL // This will overwrite the current mock url at localhost
s.Verbose = true // This will show you some fancy debug output
// s.HTTPRecording = true // This will record the request and response payloads
err := s.SomeExchangeEndpointFunction()
// check error
}
```
+ The payload should be the same.
### Please click GoDocs chevron above to view current GoDoc information for this package
## Contribution
Please feel free to submit any pull requests or suggest any desired features to be added.
When submitting a PR, please abide by our coding guidelines:
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
+ Pull requests need to be based on and opened against the `master` branch.
## Donations
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

71
exchanges/mock/common.go Normal file
View File

@@ -0,0 +1,71 @@
package mock
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/url"
"reflect"
"strconv"
"strings"
)
// MatchURLVals matches url.Value query strings
func MatchURLVals(v1, v2 url.Values) bool {
if len(v1) != len(v2) {
return false
}
if len(v1) == 0 && len(v2) == 0 {
return true
}
for key, val := range v1 {
if key == "nonce" || key == "signature" || key == "timestamp" || key == "tonce" || key == "key" { // delta values
if _, ok := v2[key]; !ok {
return false
}
continue
}
if val2, ok := v2[key]; ok {
if strings.Join(val2, "") == strings.Join(val, "") {
continue
}
}
return false
}
return true
}
// DeriveURLValsFromJSONMap gets url vals from a map[string]string encoded JSON body
func DeriveURLValsFromJSONMap(payload []byte) (url.Values, error) {
var vals = url.Values{}
if string(payload) == "" {
return vals, nil
}
intermediary := make(map[string]interface{})
err := json.Unmarshal(payload, &intermediary)
if err != nil {
return vals, err
}
for k, v := range intermediary {
switch val := v.(type) {
case string:
vals.Add(k, val)
case bool:
vals.Add(k, strconv.FormatBool(val))
case float64:
vals.Add(k, strconv.FormatFloat(val, 'f', -1, 64))
case map[string]interface{}, []interface{}, nil:
vals.Add(k, fmt.Sprintf("%v", val))
default:
log.Println(reflect.TypeOf(val))
return vals, errors.New("unhandled conversion type, please add as needed")
}
}
return vals, nil
}

View File

@@ -0,0 +1,140 @@
package mock
import (
"encoding/json"
"net/url"
"testing"
)
func TestMatchURLVals(t *testing.T) {
testVal, testVal2, testVal3, emptyVal := url.Values{}, url.Values{}, url.Values{}, url.Values{}
testVal.Add("test", "test")
testVal2.Add("test2", "test2")
testVal3.Add("test", "diferentValString")
nonceVal1, nonceVal2 := url.Values{}, url.Values{}
nonceVal1.Add("nonce", "012349723587")
nonceVal2.Add("nonce", "9327373874")
var expected = false
received := MatchURLVals(testVal, emptyVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(emptyVal, testVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal, testVal2)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal2, testVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal, testVal3)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(nonceVal1, testVal2)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
expected = true
received = MatchURLVals(emptyVal, emptyVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal, testVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(nonceVal1, nonceVal2)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
}
func TestDeriveURLValsFromJSON(t *testing.T) {
test1 := struct {
Things []string `json:"things"`
Data struct {
Numbers []int `json:"numbers"`
Number float64 `json:"number"`
SomeString string `json:"somestring"`
} `json:"data"`
}{
Things: []string{"hello", "world"},
Data: struct {
Numbers []int `json:"numbers"`
Number float64 `json:"number"`
SomeString string `json:"somestring"`
}{
Numbers: []int{1, 3, 3, 7},
Number: 3.14,
SomeString: "hello, peoples",
},
}
payload, err := json.Marshal(test1)
if err != nil {
t.Error("Test Failed - marshal error", err)
}
_, err = DeriveURLValsFromJSONMap(payload)
if err != nil {
t.Error("Test Failed - DeriveURLValsFromJSON error", err)
}
test2 := map[string]string{
"val": "1",
"val2": "2",
"val3": "3",
"val4": "4",
"val5": "5",
"val6": "6",
"val7": "7",
}
payload, err = json.Marshal(test2)
if err != nil {
t.Error("Test Failed - marshal error", err)
}
vals, err := DeriveURLValsFromJSONMap(payload)
if err != nil {
t.Error("Test Failed - DeriveURLValsFromJSON error", err)
}
if vals["val"][0] != "1" {
t.Error("Test Failed - DeriveURLValsFromJSON unexpected value",
vals["val"][0])
}
}

440
exchanges/mock/recording.go Normal file
View File

@@ -0,0 +1,440 @@
package mock
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
)
// HTTPResponse defines expected response from the end point including request
// data for pathing on the VCR server
type HTTPResponse struct {
Data json.RawMessage `json:"data"`
QueryString string `json:"queryString"`
BodyParams string `json:"bodyParams"`
Headers map[string][]string `json:"headers"`
}
// HTTPRecord will record the request and response to a default JSON file for
// mocking purposes
func HTTPRecord(res *http.Response, service string, respContents []byte) error {
if res == nil {
return errors.New("http.Response cannot be nil")
}
if res.Request == nil {
return errors.New("http.Request cannot be nil")
}
if res.Request.Method == "" {
return errors.New("request method not supplied")
}
if service == "" {
return errors.New("service not supplied cannot access correct mock file")
}
service = strings.ToLower(service)
fileout := filepath.Join(DefaultDirectory, service, service+".json")
contents, err := ioutil.ReadFile(fileout)
if err != nil {
return err
}
var m VCRMock
err = json.Unmarshal(contents, &m)
if err != nil {
return err
}
if m.Routes == nil {
m.Routes = make(map[string]map[string][]HTTPResponse)
}
var httpResponse HTTPResponse
cleanedContents, err := CheckResponsePayload(respContents)
if err != nil {
return err
}
err = json.Unmarshal(cleanedContents, &httpResponse.Data)
if err != nil {
return err
}
var body string
if res.Request.GetBody != nil {
bodycopy, bodyErr := res.Request.GetBody()
if bodyErr != nil {
return bodyErr
}
payload, bodyErr := ioutil.ReadAll(bodycopy)
if bodyErr != nil {
return bodyErr
}
body = string(payload)
}
switch res.Request.Header.Get(contentType) {
case applicationURLEncoded:
vals, urlErr := url.ParseQuery(body)
if urlErr != nil {
return urlErr
}
httpResponse.BodyParams, urlErr = GetFilteredURLVals(vals)
if urlErr != nil {
return urlErr
}
case textPlain:
payload := res.Request.Header.Get("X-Gemini-Payload")
j, dErr := crypto.Base64Decode(payload)
if dErr != nil {
return dErr
}
httpResponse.BodyParams = string(j)
default:
httpResponse.BodyParams = body
}
httpResponse.Headers, err = GetFilteredHeader(res)
if err != nil {
return err
}
httpResponse.QueryString, err = GetFilteredURLVals(res.Request.URL.Query())
if err != nil {
return err
}
_, ok := m.Routes[res.Request.URL.Path]
if !ok {
m.Routes[res.Request.URL.Path] = make(map[string][]HTTPResponse)
m.Routes[res.Request.URL.Path][res.Request.Method] = []HTTPResponse{httpResponse}
} else {
mockResponses, ok := m.Routes[res.Request.URL.Path][res.Request.Method]
if !ok {
m.Routes[res.Request.URL.Path][res.Request.Method] = []HTTPResponse{httpResponse}
} else {
switch res.Request.Method { // Based off method - check add or replace
case http.MethodGet:
for i := range mockResponses {
mockQuery, urlErr := url.ParseQuery(mockResponses[i].QueryString)
if urlErr != nil {
return urlErr
}
if MatchURLVals(mockQuery, res.Request.URL.Query()) {
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...) // Delete Old
break
}
}
case http.MethodPost:
for i := range mockResponses {
cType, ok := mockResponses[i].Headers[contentType]
if !ok {
return errors.New("cannot find content type within mock responses")
}
jCType := strings.Join(cType, "")
var found bool
switch jCType {
case applicationURLEncoded:
respQueryVals, urlErr := url.ParseQuery(body)
if urlErr != nil {
return urlErr
}
mockRespVals, urlErr := url.ParseQuery(mockResponses[i].BodyParams)
if urlErr != nil {
log.Fatal(urlErr)
}
if MatchURLVals(respQueryVals, mockRespVals) {
// if found will delete instance and overwrite with new
// data
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...)
found = true
}
case applicationJSON, textPlain:
reqVals, jErr := DeriveURLValsFromJSONMap([]byte(body))
if jErr != nil {
return jErr
}
mockVals, jErr := DeriveURLValsFromJSONMap([]byte(mockResponses[i].BodyParams))
if jErr != nil {
return jErr
}
if MatchURLVals(reqVals, mockVals) {
// if found will delete instance and overwrite with new
// data
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...)
found = true
}
default:
return fmt.Errorf("unhandled content type %s", jCType)
}
if found {
break
}
}
default:
return fmt.Errorf("unhandled request method %s", res.Request.Method)
}
m.Routes[res.Request.URL.Path][res.Request.Method] = append(mockResponses, httpResponse)
}
}
payload, err := json.MarshalIndent(m, "", " ")
if err != nil {
return err
}
return common.WriteFile(fileout, payload)
}
// GetFilteredHeader filters excluded http headers for insertion into a mock
// test file
func GetFilteredHeader(res *http.Response) (http.Header, error) {
items, err := GetExcludedItems()
if err != nil {
return res.Header, err
}
for i := range items.Headers {
if res.Request.Header.Get(items.Headers[i]) != "" {
res.Request.Header.Set(items.Headers[i], "")
}
}
return res.Request.Header, nil
}
// GetFilteredURLVals filters excluded url value variables for insertion into a
// mock test file
func GetFilteredURLVals(vals url.Values) (string, error) {
items, err := GetExcludedItems()
if err != nil {
return "", err
}
for key, val := range vals {
for i := range items.Variables {
if strings.EqualFold(items.Variables[i], val[0]) {
vals.Set(key, "")
}
}
}
return vals.Encode(), nil
}
// CheckResponsePayload checks to see if there are any response body variables
// that should not be there.
func CheckResponsePayload(data []byte) ([]byte, error) {
items, err := GetExcludedItems()
if err != nil {
return nil, err
}
var intermediary interface{}
err = json.Unmarshal(data, &intermediary)
if err != nil {
return nil, err
}
payload, err := CheckJSON(intermediary, &items)
if err != nil {
return nil, err
}
return json.MarshalIndent(payload, "", " ")
}
// Reflection consts
const (
Int64 = "int64"
Float64 = "float64"
Slice = "slice"
String = "string"
Bool = "bool"
Invalid = "invalid"
)
// CheckJSON recursively parses json data to retract keywords, quite intensive.
func CheckJSON(data interface{}, excluded *Exclusion) (interface{}, error) {
var context map[string]interface{}
if reflect.TypeOf(data).String() == "[]interface {}" {
var sData []interface{}
for i := range data.([]interface{}) {
checkedData, err := CheckJSON(data.([]interface{})[i], excluded)
if err != nil {
return nil, err
}
sData = append(sData, checkedData)
}
return sData, nil
}
conv, err := json.Marshal(data)
if err != nil {
return nil, err
}
err = json.Unmarshal(conv, &context)
if err != nil {
return nil, err
}
if len(context) == 0 {
// Nil for some reason, should error out before in json.Unmarshal
return nil, nil
}
for key, val := range context {
switch reflect.ValueOf(val).Kind().String() {
case String:
if IsExcluded(key, excluded.Variables) {
context[key] = "" // Zero val string
}
case Int64:
if IsExcluded(key, excluded.Variables) {
context[key] = 0 // Zero val int
}
case Float64:
if IsExcluded(key, excluded.Variables) {
context[key] = 0.0 // Zero val float
}
case Slice:
slice := val.([]interface{})
if len(slice) < 1 {
// Empty slice found
context[key] = slice
} else {
if _, ok := slice[0].(map[string]interface{}); ok {
var cleanSlice []interface{}
for i := range slice {
cleanMap, sErr := CheckJSON(slice[i], excluded)
if sErr != nil {
return nil, sErr
}
cleanSlice = append(cleanSlice, cleanMap)
}
context[key] = cleanSlice
} else if IsExcluded(key, excluded.Variables) {
context[key] = nil // Zero val slice
}
}
case Bool, Invalid: // Skip these bad boys for now
default:
// Recursively check map data
contextValue, err := CheckJSON(val, excluded)
if err != nil {
return nil, err
}
context[key] = contextValue
}
}
return context, nil
}
// IsExcluded cross references the key with the excluded variables
func IsExcluded(key string, excludedVars []string) bool {
for i := range excludedVars {
if strings.EqualFold(key, excludedVars[i]) {
return true
}
}
return false
}
var excludedList Exclusion
var m sync.Mutex
var set bool
var exclusionFile = DefaultDirectory + "exclusion.json"
var defaultExcludedHeaders = []string{"Key",
"X-Mbx-Apikey",
"Rest-Key",
"Apiauth-Key"}
var defaultExcludedVariables = []string{"bsb",
"user",
"name",
"real_name",
"receiver_name",
"account_number",
"username"}
// Exclusion defines a list of items to be excluded from the main mock output
// this attempts a catch all approach and needs to be updated per exchange basis
type Exclusion struct {
Headers []string `json:"headers"`
Variables []string `json:"variables"`
}
// GetExcludedItems checks to see if the variable is in the exclusion list as to
// not display secure items in mock file generator output
func GetExcludedItems() (Exclusion, error) {
m.Lock()
defer m.Unlock()
if !set {
file, err := ioutil.ReadFile(exclusionFile)
if err != nil {
if !strings.Contains(err.Error(), "no such file or directory") {
return excludedList, err
}
excludedList.Headers = defaultExcludedHeaders
excludedList.Variables = defaultExcludedVariables
data, mErr := json.MarshalIndent(excludedList, "", " ")
if mErr != nil {
return excludedList, mErr
}
mErr = ioutil.WriteFile(exclusionFile, data, os.ModePerm)
if mErr != nil {
return excludedList, mErr
}
} else {
err = json.Unmarshal(file, &excludedList)
if err != nil {
return excludedList, err
}
if len(excludedList.Headers) == 0 || len(excludedList.Variables) == 0 {
return excludedList, errors.New("exclusion list does not have names")
}
}
set = true
}
return excludedList, nil
}

View File

@@ -0,0 +1,186 @@
package mock
import (
"encoding/json"
"net/http"
"net/url"
"strings"
"testing"
)
func TestGetFilteredHeader(t *testing.T) {
resp := http.Response{}
resp.Request = &http.Request{}
resp.Request.Header = http.Header{}
resp.Request.Header.Set("Key", "RiskyVals")
fMap, err := GetFilteredHeader(&resp)
if err != nil {
t.Error(err)
}
if fMap.Get("Key") != "" {
t.Error("Test Failed - risky vals where not replaced correctly")
}
}
func TestGetFilteredURLVals(t *testing.T) {
superSecretData := "Dr Seuss"
shadyVals := url.Values{}
shadyVals.Set("real_name", superSecretData)
cleanVals, err := GetFilteredURLVals(shadyVals)
if err != nil {
t.Error("Test Failed - GetFilteredURLVals error", err)
}
if strings.Contains(cleanVals, superSecretData) {
t.Error("Test Failed - Super secret data found")
}
}
func TestCheckResponsePayload(t *testing.T) {
testbody := struct {
SomeJSON string `json:"stuff"`
}{
SomeJSON: "REAAAAHHHHH",
}
payload, err := json.Marshal(testbody)
if err != nil {
t.Fatal("Test Failed - json marshal error", err)
}
data, err := CheckResponsePayload(payload)
if err != nil {
t.Error("Test Failed - CheckBody error", err)
}
expected := `{
"stuff": "REAAAAHHHHH"
}`
if string(data) != expected {
t.Error("unexpected returned data")
}
}
type TestStructLevel0 struct {
StringVal string `json:"stringVal"`
FloatVal float64 `json:"floatVal"`
IntVal int64 `json:"intVal"`
StructVal TestStructLevel1 `json:"structVal"`
}
type TestStructLevel1 struct {
OkayVal string `json:"okayVal"`
OkayVal2 float64 `json:"okayVal2"`
BadVal string `json:"user"`
BadVal2 int `json:"bsb"`
OtherData TestStructLevel2 `json:"otherVals"`
}
type TestStructLevel2 struct {
OkayVal string `json:"okayVal"`
OkayVal2 float64 `json:"okayVal2"`
BadVal float32 `json:"name"`
BadVal2 int32 `json:"real_name"`
OtherData TestStructLevel3 `json:"moreOtherVals"`
}
type TestStructLevel3 struct {
OkayVal string `json:"okayVal"`
OkayVal2 float64 `json:"okayVal2"`
BadVal int64 `json:"receiver_name"`
BadVal2 string `json:"account_number"`
}
func TestCheckJSON(t *testing.T) {
level3 := TestStructLevel3{
OkayVal: "stuff",
OkayVal2: 129219,
BadVal: 1337,
BadVal2: "Super Secret Password",
}
level2 := TestStructLevel2{
OkayVal: "stuff",
OkayVal2: 129219,
BadVal: 0.222,
BadVal2: 1337888888,
OtherData: level3,
}
level1 := TestStructLevel1{
OkayVal: "stuff",
OkayVal2: 120938,
BadVal: "CritcalBankingStuff",
BadVal2: 1337,
OtherData: level2,
}
testVal := TestStructLevel0{
StringVal: "somestringstuff",
FloatVal: 3.14,
IntVal: 1337,
StructVal: level1,
}
exclusionList, err := GetExcludedItems()
if err != nil {
t.Error("Test Failed - GetExcludedItems error", err)
}
vals, err := CheckJSON(testVal, &exclusionList)
if err != nil {
t.Error("Test Failed - Check JSON error", err)
}
payload, err := json.Marshal(vals)
if err != nil {
t.Fatal("Test Failed - json marshal error", err)
}
newStruct := TestStructLevel0{}
err = json.Unmarshal(payload, &newStruct)
if err != nil {
t.Fatal("Test Failed - Umarshal error", err)
}
if newStruct.StructVal.BadVal != "" {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.BadVal2 != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.BadVal != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.BadVal2 != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.OtherData.BadVal != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.OtherData.BadVal2 != "" {
t.Error("Value not wiped correctly")
}
}
func TestGetExcludedItems(t *testing.T) {
exclusionList, err := GetExcludedItems()
if err != nil {
t.Error("Test Failed - GetExcludedItems error", err)
}
if len(exclusionList.Headers) == 0 {
t.Error("Test Failed - Header exclusion list not popoulated")
}
if len(exclusionList.Variables) == 0 {
t.Error("Test Failed - Variable exclusion list not popoulated")
}
}

283
exchanges/mock/server.go Normal file
View File

@@ -0,0 +1,283 @@
package mock
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
)
// DefaultDirectory defines the main mock directory
const DefaultDirectory = "../../testdata/http_mock/"
const (
contentType = "Content-Type"
applicationURLEncoded = "application/x-www-form-urlencoded"
applicationJSON = "application/json"
textPlain = "text/plain"
)
// VCRMock defines the main mock JSON file and attributes
type VCRMock struct {
Routes map[string]map[string][]HTTPResponse `json:"routes"`
}
// NewVCRServer starts a new VCR server for replaying HTTP requests for testing
// purposes and returns the server connection details
func NewVCRServer(path string) (string, *http.Client, error) {
if path == "" {
return "", nil, errors.New("no path to json mock file found")
}
var mockFile VCRMock
contents, err := ioutil.ReadFile(path)
if err != nil {
pathing := strings.Split(path, "/")
dirPathing := pathing[:len(pathing)-1]
dir := strings.Join(dirPathing, "/")
err = common.CreateDir(dir)
if err != nil {
return "", nil, err
}
data, jErr := json.MarshalIndent(mockFile, "", " ")
if jErr != nil {
return "", nil, jErr
}
err = common.WriteFile(path, data)
if err != nil {
return "", nil, err
}
contents = data
}
if !json.Valid(contents) {
return "",
nil,
fmt.Errorf("contents of file %s are not valid JSON", path)
}
// Get mocking data for the specific service
err = json.Unmarshal(contents, &mockFile)
if err != nil {
return "", nil, err
}
newMux := http.NewServeMux()
// Range over routes and assign responses to explicit paths and http
// methods
if len(mockFile.Routes) != 0 {
for pattern, mockResponses := range mockFile.Routes {
RegisterHandler(pattern, mockResponses, newMux)
}
} else {
newMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
err := json.NewEncoder(w).Encode("There is no mock data available in file please record a new HTTP response. Please follow README.md in the mock package.")
if err != nil {
panic(err)
}
})
}
tlsServer := httptest.NewTLSServer(newMux)
return tlsServer.URL, tlsServer.Client(), nil
}
// RegisterHandler registers a generalised mock response logic for specific
// routes
func RegisterHandler(pattern string, mock map[string][]HTTPResponse, mux *http.ServeMux) {
mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
httpResponses, ok := mock[r.Method]
if !ok {
log.Fatalf("Mock Test Failure - Method %s not present in mock file",
r.Method)
}
switch r.Method {
case http.MethodGet:
vals, err := url.ParseRequestURI(r.RequestURI)
if err != nil {
log.Fatal("Mock Test Failure - Parse request URI error", err)
}
payload, err := MatchAndGetResponse(httpResponses, vals.Query(), true)
if err != nil {
log.Fatalf("Mock Test Failure - MatchAndGetResponse error %s for %s",
err, r.RequestURI)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case http.MethodPost:
switch r.Header.Get(contentType) {
case applicationURLEncoded:
readBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal("Mock Test Failure - ReadAll error", err)
}
vals, err := url.ParseQuery(string(readBody))
if err != nil {
log.Fatal("Mock Test Failure - parse query error", err)
}
payload, err := MatchAndGetResponse(httpResponses, vals, false)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case "":
payload, err := MatchAndGetResponse(httpResponses, r.URL.Query(), true)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case applicationJSON:
readBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatalf("Mock Test Failure - %v", err)
}
reqVals, err := DeriveURLValsFromJSONMap(readBody)
if err != nil {
log.Fatalf("Mock Test Failure - %v", err)
}
payload, err := MatchAndGetResponse(httpResponses, reqVals, false)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case textPlain:
headerData, ok := r.Header["X-Gemini-Payload"]
if !ok {
log.Fatal("Mock Test Failure - Cannot find header in request")
}
base64data := strings.Join(headerData, "")
jsonThings, err := crypto.Base64Decode(base64data)
if err != nil {
log.Fatal("Mock Test Failure - ", err)
}
reqVals, err := DeriveURLValsFromJSONMap(jsonThings)
if err != nil {
log.Fatalf("Mock Test Failure - %v", err)
}
payload, err := MatchAndGetResponse(httpResponses, reqVals, false)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
default:
log.Fatalf("Mock Test Failure - Unhandled content type %v",
r.Header.Get(contentType))
}
case http.MethodDelete:
payload, err := MatchAndGetResponse(httpResponses, r.URL.Query(), true)
if err != nil {
log.Println(r.URL.Query())
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
default:
log.Fatal("Mock Test Failure - Unhandled HTTP method:",
r.Header.Get(contentType))
}
})
}
// MessageWriteJSON writes JSON to a connection
func MessageWriteJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set(contentType, applicationJSON)
w.WriteHeader(status)
if data != nil {
err := json.NewEncoder(w).Encode(data)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
log.Fatal("Mock Test Failure - JSON encode error", err)
}
}
}
// MatchAndGetResponse matches incoming request values with mockdata response
// values and returns the payload
func MatchAndGetResponse(mockData []HTTPResponse, requestVals url.Values, isQueryData bool) (json.RawMessage, error) {
for i := range mockData {
var data string
if isQueryData {
data = mockData[i].QueryString
} else {
data = mockData[i].BodyParams
}
var mockVals = url.Values{}
var err error
if json.Valid([]byte(data)) {
something := make(map[string]interface{})
err = json.Unmarshal([]byte(data), &something)
if err != nil {
return nil, err
}
for k, v := range something {
switch val := v.(type) {
case string:
mockVals.Add(k, val)
case bool:
mockVals.Add(k, strconv.FormatBool(val))
case float64:
mockVals.Add(k, strconv.FormatFloat(val, 'f', -1, 64))
case map[string]interface{}, []interface{}, nil:
mockVals.Add(k, fmt.Sprintf("%v", val))
default:
log.Println(reflect.TypeOf(val))
log.Fatal("unhandled type please add as needed")
}
}
} else {
mockVals, err = url.ParseQuery(data)
if err != nil {
return nil, err
}
}
if MatchURLVals(mockVals, requestVals) {
return mockData[i].Data, nil
}
}
return nil, errors.New("no data could be matched")
}

View File

@@ -0,0 +1,117 @@
package mock
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
)
type responsePayload struct {
Price float64 `json:"price"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
}
const queryString = "currency=btc&command=getprice"
const testFile = "test.json"
func TestNewVCRServer(t *testing.T) {
_, _, err := NewVCRServer("")
if err == nil {
t.Error("Test Failed - NewVCRServer error cannot be nil")
}
// Set up mock data
test1 := VCRMock{}
test1.Routes = make(map[string]map[string][]HTTPResponse)
test1.Routes["/test"] = make(map[string][]HTTPResponse)
rp, err := json.Marshal(responsePayload{Price: 8000.0,
Amount: 1,
Currency: "bitcoin"})
if err != nil {
t.Fatal("Test Failed - marshal error", err)
}
testValue := HTTPResponse{Data: rp, QueryString: queryString, BodyParams: queryString}
test1.Routes["/test"][http.MethodGet] = []HTTPResponse{testValue}
payload, err := json.Marshal(test1)
if err != nil {
t.Fatal("Test Failed - marshal error", err)
}
err = ioutil.WriteFile(testFile, payload, os.ModePerm)
if err != nil {
t.Fatal("Test Failed - marshal error", err)
}
deets, client, err := NewVCRServer(testFile)
if err != nil {
t.Error("Test Failed - NewVCRServer error", err)
}
common.HTTPClient = client // Set common package global HTTP Client
_, err = common.SendHTTPRequest(http.MethodGet,
"http://localhost:300/somethingElse?"+queryString,
nil,
bytes.NewBufferString(""))
if err == nil {
t.Error("Test Failed - Sending http request expected an error")
}
// Expected good outcome
r, err := common.SendHTTPRequest(http.MethodGet,
deets,
nil,
bytes.NewBufferString(""))
if err != nil {
t.Error("Test Failed - Sending http request error", err)
}
if !strings.Contains(r, "404 page not found") {
t.Error("Test Failed - Was not expecting any value returned:", r)
}
r, err = common.SendHTTPRequest(http.MethodGet,
deets+"/test?"+queryString,
nil,
bytes.NewBufferString(""))
if err != nil {
t.Error("Test Failed - Sending http request error", err)
}
var res responsePayload
err = json.Unmarshal([]byte(r), &res)
if err != nil {
t.Error("Test Failed - unmarshal error", err)
}
if res.Price != 8000 {
t.Error("Test Failed - response error expected 8000 but received:",
res.Price)
}
if res.Amount != 1 {
t.Error("Test Failed - response error expected 1 but received:",
res.Amount)
}
if res.Currency != "bitcoin" {
t.Error("Test Failed - response error expected \"bitcoin\" but received:",
res.Currency)
}
// clean up test.json file
err = os.Remove(testFile)
if err != nil {
t.Fatal("Test Failed - Remove error", err)
}
}

View File

@@ -576,7 +576,15 @@ func (o *OKGroup) SendHTTPRequest(httpMethod, requestType, requestPath string, d
errCap := errCapFormat{}
errCap.Result = true
err = o.SendPayload(strings.ToUpper(httpMethod), path, headers, bytes.NewBuffer(payload), &intermediary, authenticated, false, o.Verbose, o.HTTPDebugging)
err = o.SendPayload(strings.ToUpper(httpMethod),
path, headers,
bytes.NewBuffer(payload),
&intermediary,
authenticated,
false,
o.Verbose,
o.HTTPDebugging,
o.HTTPRecording)
if err != nil {
return err
}

View File

@@ -224,8 +224,8 @@ func (p *Poloniex) GetLoanOrders(currency string) (LoanOrders, error) {
// GetBalances returns balances for your account.
func (p *Poloniex) GetBalances() (Balance, error) {
var result interface{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalances, url.Values{}, &result)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalances, url.Values{}, &result)
if err != nil {
return Balance{}, err
}
@@ -244,8 +244,8 @@ func (p *Poloniex) GetBalances() (Balance, error) {
// GetCompleteBalances returns complete balances from your account.
func (p *Poloniex) GetCompleteBalances() (CompleteBalances, error) {
var result interface{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalancesComplete, url.Values{}, &result)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalancesComplete, url.Values{}, &result)
if err != nil {
return CompleteBalances{}, err
}
@@ -270,14 +270,18 @@ func (p *Poloniex) GetCompleteBalances() (CompleteBalances, error) {
func (p *Poloniex) GetDepositAddresses() (DepositAddresses, error) {
var result interface{}
addresses := DepositAddresses{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexDepositAddresses, url.Values{}, &result)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexDepositAddresses, url.Values{}, &result)
if err != nil {
return addresses, err
}
addresses.Addresses = make(map[string]string)
data := result.(map[string]interface{})
data, ok := result.(map[string]interface{})
if !ok {
return addresses, errors.New("return val not map[string]interface{}")
}
for x, y := range data {
addresses.Addresses[x] = y.(string)
}
@@ -297,7 +301,6 @@ func (p *Poloniex) GenerateNewAddress(currency string) (string, error) {
values.Set("currency", currency)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexGenerateNewAddress, values, &resp)
if err != nil {
return "", err
}
@@ -498,7 +501,6 @@ func (p *Poloniex) Withdraw(currency, address string, amount float64) (bool, err
values.Set("address", address)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexWithdraw, values, &result)
if err != nil {
return false, err
}
@@ -525,7 +527,6 @@ func (p *Poloniex) GetTradableBalances() (map[string]map[string]float64, error)
result := Response{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexTradableBalances, url.Values{}, &result.Data)
if err != nil {
return nil, err
}
@@ -553,7 +554,6 @@ func (p *Poloniex) TransferBalance(currency, from, to string, amount float64) (b
values.Set("toAccount", to)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexTransferBalance, values, &result)
if err != nil {
return false, err
}
@@ -619,7 +619,6 @@ func (p *Poloniex) CloseMarginPosition(currency string) (bool, error) {
result := GenericResponse{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexMarginPositionClose, values, &result)
if err != nil {
return false, err
}
@@ -655,7 +654,6 @@ func (p *Poloniex) CreateLoanOffer(currency string, amount, rate float64, durati
result := Response{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexCreateLoanOffer, values, &result)
if err != nil {
return 0, err
}
@@ -674,7 +672,6 @@ func (p *Poloniex) CancelLoanOffer(orderNumber int64) (bool, error) {
values.Set("orderID", strconv.FormatInt(orderNumber, 10))
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexCancelLoanOffer, values, &result)
if err != nil {
return false, err
}
@@ -694,7 +691,6 @@ func (p *Poloniex) GetOpenLoanOffers() (map[string][]LoanOffer, error) {
result := Response{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexOpenLoanOffers, url.Values{}, &result.Data)
if err != nil {
return nil, err
}
@@ -725,15 +721,10 @@ func (p *Poloniex) GetLendingHistory(start, end string) ([]LendingHistory, error
}
var resp []LendingHistory
err := p.SendAuthenticatedHTTPRequest(http.MethodPost,
return resp, p.SendAuthenticatedHTTPRequest(http.MethodPost,
poloniexLendingHistory,
vals,
&resp)
if err != nil {
return nil, err
}
return resp, nil
}
// ToggleAutoRenew allows for the autorenew of a contract
@@ -746,7 +737,6 @@ func (p *Poloniex) ToggleAutoRenew(orderNumber int64) (bool, error) {
poloniexAutoRenew,
values,
&result)
if err != nil {
return false, err
}
@@ -768,7 +758,8 @@ func (p *Poloniex) SendHTTPRequest(path string, result interface{}) error {
false,
false,
p.Verbose,
p.HTTPDebugging)
p.HTTPDebugging,
p.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -777,13 +768,11 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
p.Name)
}
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
headers["Key"] = p.API.Credentials.Key
n := p.Requester.GetNonce(true).String()
values.Set("nonce", n)
values.Set("nonce", p.Requester.GetNonce(true).String())
values.Set("command", endpoint)
hmac := crypto.GetHMAC(crypto.HashSHA512,
@@ -802,7 +791,8 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values
true,
true,
p.Verbose,
p.HTTPDebugging)
p.HTTPDebugging,
p.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -0,0 +1,32 @@
//+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 poloniex
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()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
log.Fatal("Test Failed - Poloniex Setup() init error", err)
}
poloniexConfig.API.AuthenticatedSupport = true
poloniexConfig.API.Credentials.Key = apiKey
poloniexConfig.API.Credentials.Secret = apiSecret
p.SetDefaults()
p.Setup(poloniexConfig)
log.Printf(sharedtestvalues.LiveTesting, p.GetName(), p.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,45 @@
//+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 poloniex
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/poloniex/poloniex.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
log.Fatal("Test Failed - Poloniex Setup() init error", err)
}
p.SkipAuthCheck = true
poloniexConfig.API.AuthenticatedSupport = true
poloniexConfig.API.Credentials.Key = apiKey
poloniexConfig.API.Credentials.Secret = apiSecret
p.SetDefaults()
p.Setup(poloniexConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
p.HTTPClient = newClient
p.API.Endpoints.URL = serverDetails
log.Printf(sharedtestvalues.MockTesting, p.GetName(), p.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -7,15 +7,12 @@ import (
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
)
var p Poloniex
// Please supply your own APIKEYS here for due diligence testing
const (
apiKey = ""
@@ -23,31 +20,17 @@ const (
canManipulateRealOrders = false
)
var isSetup bool
var p Poloniex
func TestSetup(t *testing.T) {
if !isSetup {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
t.Error("Test Failed - Poloniex Setup() init error")
}
poloniexConfig.API.AuthenticatedSupport = true
poloniexConfig.API.AuthenticatedWebsocketSupport = true
poloniexConfig.API.Credentials.Key = apiKey
poloniexConfig.API.Credentials.Secret = apiSecret
p.SetDefaults()
p.Setup(poloniexConfig)
isSetup = true
}
func areTestAPIKeysSet() bool {
return p.ValidateAPICredentials()
}
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := p.GetTicker()
if err != nil {
t.Error("Test faild - Poloniex GetTicker() error")
t.Error("Test Failed - Poloniex GetTicker() error", err)
}
}
@@ -115,37 +98,43 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
p.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.CryptocurrencyTradeFee,
feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
TestSetup(t)
var feeBuilder = setFeeBuilder()
if areTestAPIKeysSet() {
if areTestAPIKeysSet() || mockTests {
// CryptocurrencyTradeFee Basic
if resp, err := p.GetFee(feeBuilder); resp != float64(0.002) || err != nil {
if resp, err := p.GetFee(feeBuilder); resp != float64(0.0025) || err != nil {
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.0015), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.0025), resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := p.GetFee(feeBuilder); resp != float64(2000) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(2000), resp)
if resp, err := p.GetFee(feeBuilder); resp != float64(2500) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(2500), resp)
t.Error(err)
}
@@ -153,7 +142,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
}
@@ -161,7 +151,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.001), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.001), resp)
t.Error(err)
}
@@ -170,7 +161,8 @@ func TestGetFee(t *testing.T) {
feeBuilder.Pair.Base = currency.NewCode("hello")
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
@@ -178,7 +170,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
@@ -186,7 +179,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
@@ -195,66 +189,67 @@ func TestGetFee(t *testing.T) {
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.USD
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
t.Parallel()
TestSetup(t)
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText +
" & " +
exchange.NoFiatWithdrawalsText
withdrawPermissions := p.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := p.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")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetActiveOrders() error", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Test Failed - Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetActiveOrders() err", err)
}
}
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := p.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not mock get order history: %s", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
return p.ValidateAPICredentials()
}
func TestSubmitOrder(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -265,51 +260,50 @@ func TestSubmitOrder(t *testing.T) {
Quote: currency.LTC,
},
OrderSide: exchange.BuyOrderSide,
OrderType: exchange.LimitOrderType,
Price: 1,
Amount: 1,
ClientID: "meowOrder",
OrderType: exchange.MarketOrderType,
Price: 10,
Amount: 10000000,
ClientID: "hi",
}
response, err := p.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced):
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock SubmitOrder() err", err)
}
}
func TestCancelExchangeOrder(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
err := p.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelExchangeOrder() err", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -323,14 +317,14 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := p.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelAllExchangeOrders() err", err)
}
if len(resp.OrderStatus) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.OrderStatus))
}
@@ -338,87 +332,90 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestModifyOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := p.ModifyOrder(&exchange.ModifyOrder{OrderID: "1337", Price: 1337})
if err == nil {
t.Error("Test Failed - ModifyOrder() error")
switch {
case areTestAPIKeysSet() && err != nil && mockTests:
t.Error("Test Failed - ModifyOrder() error", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Test Failed - ModifyOrder() error cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - Mock ModifyOrder() err", err)
}
}
func TestWithdraw(t *testing.T) {
t.Parallel()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
Currency: currency.BTC,
Amount: 0,
Currency: currency.LTC,
Description: "WITHDRAW IT ALL",
},
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := p.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Withdraw failed to be placed: %v", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock Withdraw() err", err)
}
}
func TestWithdrawFiat(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
var withdrawFiatRequest exchange.FiatWithdrawRequest
_, err := p.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported, err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.FiatWithdrawRequest{}
var withdrawFiatRequest exchange.FiatWithdrawRequest
_, err := p.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported, err)
}
}
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() {
_, err := p.GetDepositAddress(currency.DASH, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress()", err)
}
} else {
_, err := p.GetDepositAddress(currency.DASH, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress()")
}
_, err := p.GetDepositAddress(currency.DASH, "")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetDepositAddress()", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Test Failed - GetDepositAddress() cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetDepositAddress() err", err)
}
}
func TestWsHandleAccountData(t *testing.T) {
t.Parallel()
TestSetup(t)
p.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
jsons := []string{
`[["n",225,807230187,0,"1000.00000000","0.10000000","2018-11-07 16:42:42"],["b",267,"e","-0.10000000"]]`,
@@ -438,7 +435,7 @@ func TestWsHandleAccountData(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
// Will receive a message only on failure
func TestWsAuth(t *testing.T) {
TestSetup(t)
t.Parallel()
if !p.Websocket.IsEnabled() && !p.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}

View File

@@ -457,7 +457,7 @@ func (p *Poloniex) GetWebsocket() (*wshandler.Websocket, error) {
// GetFeeByType returns an estimate of fee based on type of transaction
func (p *Poloniex) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !p.AllowAuthenticatedRequest() && // Todo check connection status
if (!p.AllowAuthenticatedRequest() || p.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}

View File

@@ -15,6 +15,7 @@ import (
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/nonce"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
@@ -81,6 +82,7 @@ type Job struct {
AuthRequest bool
Verbose bool
HTTPDebugging bool
Record bool
}
// NewRateLimit creates a new RateLimit
@@ -279,14 +281,21 @@ func (r *Requester) checkRequest(method, path string, body io.Reader, headers ma
}
// DoRequest performs a HTTP/HTTPS request with the supplied params
func (r *Requester) DoRequest(req *http.Request, path string, body io.Reader, result interface{}, authRequest, verbose, httpDebug bool) error {
func (r *Requester) DoRequest(req *http.Request, path string, body io.Reader, result interface{}, authRequest, verbose, httpDebug, httpRecord bool) error {
if verbose {
log.Debugf(log.Global,
"%s exchange request path: %s requires rate limiter: %v", r.Name, path, r.RequiresRateLimiter())
"%s exchange request path: %s requires rate limiter: %v",
r.Name,
path,
r.RequiresRateLimiter())
for k, d := range req.Header {
log.Debugf(log.Global, "%s exchange request header [%s]: %s", r.Name, k, d)
}
log.Debugln(log.Global, body)
log.Debugf(log.Global,
"%s exchange request type: %s", r.Name, req.Method)
log.Debugf(log.Global,
"%s exchange request body: %v", r.Name, body)
}
var timeoutError error
@@ -344,6 +353,14 @@ func (r *Requester) DoRequest(req *http.Request, path string, body io.Reader, re
return err
}
if httpRecord {
// This dumps http responses for future mocking implementations
err = mock.HTTPRecord(resp, r.Name, contents)
if err != nil {
return fmt.Errorf("mock recording failure %s", err)
}
}
if resp.StatusCode != 200 && resp.StatusCode != 201 && resp.StatusCode != 202 {
err = fmt.Errorf("unsuccessful HTTP status code: %d", resp.StatusCode)
if verbose {
@@ -387,7 +404,7 @@ func (r *Requester) worker() {
if !r.IsRateLimited(x.AuthRequest) {
r.IncrementRequests(x.AuthRequest)
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging)
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging, x.Record)
x.JobResult <- &JobResult{
Error: err,
Result: x.Result,
@@ -411,7 +428,7 @@ func (r *Requester) worker() {
log.Debugf(log.ExchangeSys, "%s request. No longer rate limited! Doing request", r.Name)
}
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging)
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging, x.Record)
x.JobResult <- &JobResult{
Error: err,
Result: x.Result,
@@ -424,7 +441,7 @@ func (r *Requester) worker() {
}
// SendPayload handles sending HTTP/HTTPS requests
func (r *Requester) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, nonceEnabled, verbose, httpDebugging bool) error {
func (r *Requester) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, nonceEnabled, verbose, httpDebugging, record bool) error {
if !nonceEnabled {
r.lock()
}
@@ -462,7 +479,7 @@ func (r *Requester) SendPayload(method, path string, headers map[string]string,
if !r.RequiresRateLimiter() {
r.unlock()
return r.DoRequest(req, path, body, result, authRequest, verbose, httpDebugging)
return r.DoRequest(req, path, body, result, authRequest, verbose, httpDebugging, record)
}
if len(r.Jobs) == MaxRequestJobs {
@@ -491,6 +508,7 @@ func (r *Requester) SendPayload(method, path string, headers map[string]string,
AuthRequest: authRequest,
Verbose: verbose,
HTTPDebugging: httpDebugging,
Record: record,
}
if verbose {

View File

@@ -200,7 +200,7 @@ func TestCheckRequest(t *testing.T) {
func TestDoRequest(t *testing.T) {
var test = new(Requester)
err := test.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err := test.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal("not iniitalised")
}
@@ -211,17 +211,17 @@ func TestDoRequest(t *testing.T) {
}
r.Name = "bitfinex"
err = r.SendPayload("BLAH", "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload("BLAH", "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal("unexpected values")
}
err = r.SendPayload(http.MethodGet, "", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal("unexpected values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -233,7 +233,7 @@ func TestDoRequest(t *testing.T) {
r.SetRateLimit(false, time.Second, 0)
r.SetRateLimit(true, time.Second, 0)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -250,7 +250,7 @@ func TestDoRequest(t *testing.T) {
t.Fatal("unexepcted values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -261,27 +261,27 @@ func TestDoRequest(t *testing.T) {
t.Fatal("unexepcted values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, true, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, true, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
var result interface{}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, true, false, false)
if err != nil {
t.Fatal(err)
}
headers := make(map[string]string)
headers["content-type"] = "content/text"
err = r.SendPayload(http.MethodPost, "https://bitfinex.com", headers, nil, result, false, false, true, false)
err = r.SendPayload(http.MethodPost, "https://bitfinex.com", headers, nil, result, false, false, true, false, false)
if err != nil {
t.Fatal(err)
}
r.StartCycle()
r.UnauthLimit.SetRequests(100)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, false, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, false, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -297,7 +297,7 @@ func TestDoRequest(t *testing.T) {
}
r.HTTPClient.Timeout = 1 * time.Second
err = r.SendPayload(http.MethodPost, "https://httpstat.us/200?sleep=20000", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodPost, "https://httpstat.us/200?sleep=20000", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal(err)
}
@@ -327,6 +327,6 @@ func BenchmarkRequestLockMech(b *testing.B) {
var r = new(Requester)
var meep interface{}
for n := 0; n < b.N; n++ {
r.SendPayload(http.MethodGet, "127.0.0.1", nil, nil, &meep, false, false, false, false)
r.SendPayload(http.MethodGet, "127.0.0.1", nil, nil, &meep, false, false, false, false, false)
}
}

View File

@@ -13,6 +13,9 @@ const (
// WebsocketChannelOverrideCapacity used in websocket testing
// Defines channel capacity as defaults size can block tests
WebsocketChannelOverrideCapacity = 20
MockTesting = "Mock testing framework in use for %s exchange @ %s on REST endpoints only"
LiveTesting = "Mock testing bypassed; live testing of REST endpoints in use for %s exchange @ %s"
)
// GetWebsocketInterfaceChannelOverride returns a new interface based channel

View File

@@ -23,6 +23,7 @@ var Exchanges = []string{
"itbit",
"kraken",
"lakebtc",
"lbank",
"localbitcoins",
"okcoin international",
"okex",

View File

@@ -264,7 +264,8 @@ func (y *Yobit) SendHTTPRequest(path string, result interface{}) error {
false,
false,
y.Verbose,
y.HTTPDebugging)
y.HTTPDebugging,
y.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to Yobit
@@ -305,7 +306,8 @@ func (y *Yobit) SendAuthenticatedHTTPRequest(path string, params url.Values, res
true,
true,
y.Verbose,
y.HTTPDebugging)
y.HTTPDebugging,
y.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -286,7 +286,16 @@ func (z *ZB) GetCryptoAddress(currency currency.Code) (UserAddress, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (z *ZB) SendHTTPRequest(path string, result interface{}) error {
return z.SendPayload(http.MethodGet, path, nil, nil, result, false, false, z.Verbose, z.HTTPDebugging)
return z.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
z.Verbose,
z.HTTPDebugging,
z.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the zb API
@@ -324,7 +333,8 @@ func (z *ZB) SendAuthenticatedHTTPRequest(httpMethod string, params url.Values,
true,
false,
z.Verbose,
z.HTTPDebugging)
z.HTTPDebugging,
z.HTTPRecording)
if err != nil {
return err
}

1
testdata/README.md vendored
View File

@@ -42,3 +42,4 @@ When submitting a PR, please abide by our coding guidelines:
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

View File

@@ -443,6 +443,49 @@
}
]
},
{
"name": "LBank",
"enabled": true,
"verbose": false,
"websocket": false,
"useSandbox": false,
"restPollingDelay": 10,
"httpTimeout": 15000000000,
"httpUserAgent": "",
"httpDebugging": false,
"authenticatedApiSupport": false,
"authenticatedWebsocketApiSupport": false,
"apiKey": "Key",
"apiSecret": "Secret",
"apiUrl": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"apiUrlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API",
"proxyAddress": "",
"websocketUrl": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API",
"availablePairs": "fbc_usdt,hds_usdt,galt_usdt,dxn_usdt,iog_usdt,ioex_usdt,vollar_usdt,oath_usdt,bloc_usdt,btc_lbcn,eth_lbcn,usdt_lbcn,btc_usdt,eth_usdt,eth_btc,abbc_btc,bzky_eth,onot_eth,kisc_eth,bxa_usdt,atp_usdt,mat_usdt,sky_btc,sky_lbcn,rnt_usdt,vena_usdt,grin_usdt,ida_usdt,pnt_usdt,bsv_btc,bsv_usdt,opx_usdt,tena_eth,seer_lbcn,vet_lbcn,vtho_btc,vnx_lbcn,vnx_btc,amo_eth,ubex_btc,eos_btc,ubex_usdt,tns_lbcn,tns_btc,ali_eth,sdc_eth,sait_eth,artcn_usdt,dax_btc,dax_eth,dali_usdt,vet_usdt,ten_usdt,bch_usdt,neo_usdt,qtum_usdt,zec_usdt,vet_btc,pai_btc,pnt_btc,bch_btc,ltc_btc,neo_btc,dash_btc,etc_btc,qtum_btc,zec_btc,sc_btc,bts_btc,cpx_btc,xwc_btc,fil6_btc,fil12_btc,fil36_btc,eos_usdt,ut_eth,ela_eth,vet_eth,vtho_eth,pai_eth,bfdt_eth,her_eth,ptt_eth,tac_eth,idhub_eth,ssc_eth,skm_eth,iic_eth,ply_eth,ext_eth,eos_eth,yoyow_eth,trx_eth,qtum_eth,zec_eth,bts_eth,btm_eth,mith_eth,nas_eth,man_eth,dbc_eth,bto_eth,ddd_eth,cpx_eth,cs_eth,iht_eth,tky_eth,ocn_eth,dct_eth,zpt_eth,eko_eth,mda_eth,pst_eth,xwc_eth,put_eth,pnt_eth,aac_eth,fil6_eth,fil12_eth,fil36_eth,uip_eth,seer_eth,bsb_eth,cdc_eth,grams_eth,ddmx_eth,eai_eth,inc_eth,bnb_usdt,ht_usdt,bot_eth,kbc_btc,kbc_usdt,mai_usdt,phv_usdt,hnb_usdt,gt_usdt,b91_usdt,voken_usdt,cye_usdt,brc_usdt,btc_ausd",
"enabledPairs": "eth_btc",
"baseCurrencies": "USD",
"assetTypes": "SPOT",
"supportsAutoPairUpdates": true,
"configCurrencyPairFormat": {
"uppercase": false,
"delimiter": "_"
},
"requestCurrencyPairFormat": {
"uppercase": false,
"delimiter": "_"
},
"bankAccounts": [
{
"bankName": "",
"bankAddress": "",
"accountName": "",
"accountNumber": "",
"swiftCode": "",
"iban": "",
"supportedCurrencies": ""
}
]
},
{
"name": "Bittrex",
"enabled": true,

2404
testdata/http_mock/anx/anx.json vendored Normal file

File diff suppressed because it is too large Load Diff

45467
testdata/http_mock/binance/binance.json vendored Normal file

File diff suppressed because it is too large Load Diff

42842
testdata/http_mock/bitstamp/bitstamp.json vendored Normal file

File diff suppressed because it is too large Load Diff

19
testdata/http_mock/exclusion.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"headers": [
"Key",
"X-Mbx-Apikey",
"Rest-Key",
"Apiauth-Key",
"X-Gemini-Apikey"
],
"variables": [
"bsb",
"user",
"name",
"real_name",
"receiver_name",
"account_number",
"username",
"login"
]
}

2734
testdata/http_mock/gemini/gemini.json vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

9031
testdata/http_mock/poloniex/poloniex.json vendored Normal file

File diff suppressed because it is too large Load Diff