Utilising authenticated websocket functions in exchange wrappers (#384)

* Basic concept commit

* Initial changes to support bitfinex v2. Reverts linter changes as they suck. Exports bitfinex ws types

* Adds ticker, trade and orderbook support

* Candles sub that returns no data COMPLETE

* Adds authenticated ws support

* Adds the barebones endpoints to support

* Adds more endpoints

* Even more endpoints

* minicommit to switch and test

* All the interactive types

* Adds support for simultaneous connections. Updates tests. Nothing is working

* Successfully adds place order. Moves all authenticated endpoints to new switch case

* Cancel order and modify order

* Cancel all orders, cancel multi orders

* Finalises implementation. Uses testMain

* Adds WS wrapper support for some funcs

* Fixing rebasing issues

* Replaces use of currency as a variable. Updates a lot of coinut websocket auth endpoint stuff

* Fixes some fun for loops with GetEnabledPairs

* Fixes tests impacted by currency var change

* Adds coinut support for WS functions. Replaces `order` vars with `ord`. Fixes some for loops too. Removes verbose from bitfinex

* So many panics

* I'm fixing a hole, where the panics get in, and stops my mind from wandering, where it will go

* Moves func `CanUseAuthenticatedWebsocketEndpoint` to Websocket package as it fits better. Adds test coverage of `CanUseAuthenticatedWebsocketEndpoint`

* Finishes up all of coinuts ws implementations.

* GateIO implementation

* Adds some helper funcs for types, sides and status. Adds support for huobi. Removes unnecessary type

* Adds forgotten huobi endpoint

* Fixes cancel order endpoint

* go hates my formatting and so do I

* The process to get authenticated kraken websocket to work. Uses testmain. Adds new auth channel, auth subscriptions, auth data handling. Not working yet

* Finishes open orders handling

* Mini update for status only updates

* Fixes some kraken points

* Finishes WS kraken since it doesn't work

* Unfinished commit, cleaning up types

* Finishes the const replacing

* Fixes extra GetNAmes after rebase

* An end to the cleanup. testmain for gateio

* Adds ZB support

* Bitfinex cleanup. Renamed func

* Testmain-47s for everyone!!! yayaaaaaaa

* Adds kraken websocket wrapper support

* Fixes rebase issues

* Fixes tests from rebase

* Adds test for conversion. Fixes for loop. Updates test order pricing. Fixes some poor made tests. Adds proper error handling for ws responses instead of logging them. Fixed issue where commented code ruined kraken ws.

* Fixes secret linting issues. Prioritises bitfinex channelID responses over authorised

* Fixes sloppy error/var declarations

* Fixes crazy bad logic where submit order errors weren't really considered. Parralols alphapoint/alphapoint_test.go. Removes buffer for multi-websocket comms channel.

* Removal of inline string and removal of redundant nil checkerinos

* Fixes err checks. Checks whether float has decimal. Fixes append. Drops omitempties. Parallel to some tests. Moves var declarations

* Replaces my lazy sprintfs with strconv.FormatInt(time.Now().Unix(), 10)

* Adds shiny new FullyMatched bool. Fixes coinbene buy sell consts

* Fixes oopsie with coinbene const replacement

* Fixes currency issue

* Cleans up new places that use JSONDecode

* Fixes huge panic bug from string int conversion. Adds large testtable for strings to order types

* Fixes some more strconversion issues. Fixes table test var usage. Changes mapperino name

* Added some new scenarios for number splitting

* Fixes lint issues

* negative num fix

* Typo fix

* Accuracy warning comment
This commit is contained in:
Scott
2019-12-04 14:16:23 +11:00
committed by Adrian Gallagher
parent a33ddcfa0a
commit 11a68a9bb7
96 changed files with 4112 additions and 2818 deletions

View File

@@ -1,8 +1,11 @@
package convert
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
"time"
)
@@ -77,3 +80,30 @@ func UnixMillis(t time.Time) int64 {
func RecvWindow(d time.Duration) int64 {
return int64(d) / int64(time.Millisecond)
}
// SplitFloatDecimals takes in a float64 and splits
// the decimals into their own integers
// Warning. Passing in numbers with many decimals
// can lead to a loss of accuracy
func SplitFloatDecimals(input float64) (baseNum, decimalNum int64, err error) {
if input > float64(math.MaxInt64) {
return 0, 0, errors.New("number too large to split into integers")
}
if input == float64(int64(input)) {
return int64(input), 0, nil
}
decStr := strconv.FormatFloat(input, 'f', -1, 64)
splitNum := strings.Split(decStr, ".")
baseNum, err = strconv.ParseInt(splitNum[0], 10, 64)
if err != nil {
return 0, 0, err
}
decimalNum, err = strconv.ParseInt(splitNum[1], 10, 64)
if err != nil {
return 0, 0, err
}
if baseNum < 0 {
decimalNum *= -1
}
return baseNum, decimalNum, nil
}

View File

@@ -1,6 +1,7 @@
package convert
import (
"math"
"testing"
"time"
)
@@ -149,3 +150,58 @@ func TestRecvWindow(t *testing.T) {
expectedOutput, actualOutput)
}
}
// TestSplitFloatDecimals ensures SplitFloatDecimals
// accurately splits decimals into integers
func TestSplitFloatDecimals(t *testing.T) {
x, y, err := SplitFloatDecimals(1.2)
if err != nil {
t.Error(err)
}
if x != 1 && y != 2 {
t.Error("Conversion error")
}
x, y, err = SplitFloatDecimals(123456.654321)
if err != nil {
t.Error(err)
}
if x != 123456 && y != 654321 {
t.Error("Conversion error")
}
x, y, err = SplitFloatDecimals(123.111000)
if err != nil {
t.Error(err)
}
if x != 123 && y != 111 {
t.Error("Conversion error")
}
x, y, err = SplitFloatDecimals(0123.111001)
if err != nil {
t.Error(err)
}
if x != 123 && y != 111001 {
t.Error("Conversion error")
}
x, y, err = SplitFloatDecimals(1)
if err != nil {
t.Error(err)
}
if x != 1 && y != 0 {
t.Error("Conversion error")
}
_, _, err = SplitFloatDecimals(float64(math.MaxInt64) + 1)
if err == nil {
t.Error("Expected conversion error")
}
_, _, err = SplitFloatDecimals(1797693134862315700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111)
if err == nil {
t.Error("Expected conversion error")
}
x, y, err = SplitFloatDecimals(-1.2)
if err != nil {
t.Error(err)
}
if x != -1 && y != -2 {
t.Error("Conversion error")
}
}

View File

@@ -343,10 +343,10 @@ func TestNewPairDelimiter(t *testing.T) {
// specific index
func TestNewPairFromIndex(t *testing.T) {
t.Parallel()
currency := defaultPair
curr := defaultPair
index := "BTC"
pair, err := NewPairFromIndex(currency, index)
pair, err := NewPairFromIndex(curr, index)
if err != nil {
t.Error("NewPairFromIndex() error", err)
}
@@ -362,9 +362,9 @@ func TestNewPairFromIndex(t *testing.T) {
)
}
currency = "DOGEBTC"
curr = "DOGEBTC"
pair, err = NewPairFromIndex(currency, index)
pair, err = NewPairFromIndex(curr, index)
if err != nil {
t.Error("NewPairFromIndex() error", err)
}

View File

@@ -175,19 +175,15 @@ func (s *Storage) SetupConversionRates() {
// SetDefaultFiatCurrencies assigns the default fiat currency list and adds it
// to the running list
func (s *Storage) SetDefaultFiatCurrencies(c ...Code) {
for _, currency := range c {
s.defaultFiatCurrencies = append(s.defaultFiatCurrencies, currency)
s.fiatCurrencies = append(s.fiatCurrencies, currency)
}
s.defaultFiatCurrencies = append(s.defaultFiatCurrencies, c...)
s.fiatCurrencies = append(s.fiatCurrencies, c...)
}
// SetDefaultCryptocurrencies assigns the default cryptocurrency list and adds
// it to the running list
func (s *Storage) SetDefaultCryptocurrencies(c ...Code) {
for _, currency := range c {
s.defaultCryptoCurrencies = append(s.defaultCryptoCurrencies, currency)
s.cryptocurrencies = append(s.cryptocurrencies, currency)
}
s.defaultCryptoCurrencies = append(s.defaultCryptoCurrencies, c...)
s.cryptocurrencies = append(s.cryptocurrencies, c...)
}
// SetupForexProviders sets up a new instance of the forex providers
@@ -507,8 +503,8 @@ func (s *Storage) GetTotalMarketCryptocurrencies() (Currencies, error) {
// IsDefaultCurrency returns if a currency is a default currency
func (s *Storage) IsDefaultCurrency(c Code) bool {
t, _ := GetTranslation(c)
for _, d := range s.defaultFiatCurrencies {
if d.Match(c) || d.Match(t) {
for i := range s.defaultFiatCurrencies {
if s.defaultFiatCurrencies[i].Match(c) || s.defaultFiatCurrencies[i].Match(t) {
return true
}
}

View File

@@ -264,11 +264,11 @@ func (o *orderManager) processOrders() {
}
for x := range result {
order := &result[x]
result := o.orderStore.Add(order)
ord := &result[x]
result := o.orderStore.Add(ord)
if result != ErrOrdersAlreadyExists {
msg := fmt.Sprintf("Order manager: Exchange %s added order ID=%v pair=%v price=%v amount=%v side=%v type=%v.",
order.Exchange, order.ID, order.CurrencyPair, order.Price, order.Amount, order.OrderSide, order.OrderType)
ord.Exchange, ord.ID, ord.CurrencyPair, ord.Price, ord.Amount, ord.OrderSide, ord.OrderType)
log.Debugf(log.OrderMgr, "%v\n", msg)
Bot.CommsManager.PushEvent(base.Event{
Type: "order",

View File

@@ -310,16 +310,17 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
}
for y := range assetTypes {
for _, p := range Bot.Exchanges[x].GetEnabledPairs(assetTypes[y]) {
enabledPairs := Bot.Exchanges[x].GetEnabledPairs(assetTypes[y])
for i := range enabledPairs {
if atomic.LoadInt32(&e.shutdown) == 1 {
return
}
if !e.exists(exchangeName, p, assetTypes[y]) {
if !e.exists(exchangeName, enabledPairs[i], assetTypes[y]) {
c := CurrencyPairSyncAgent{
AssetType: assetTypes[y],
Exchange: exchangeName,
Pair: p,
Pair: enabledPairs[i],
}
if e.Cfg.SyncTicker {
@@ -346,7 +347,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
e.add(&c)
}
c, err := e.get(exchangeName, p, assetTypes[y])
c, err := e.get(exchangeName, enabledPairs[i], assetTypes[y])
if err != nil {
log.Errorf(log.SyncMgr, "failed to get item. Err: %s\n", err)
continue
@@ -354,7 +355,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
if switchedToRest && usingWebsocket {
log.Infof(log.SyncMgr,
"%s %s: Websocket re-enabled, switching from rest to websocket\n",
c.Exchange, FormatCurrency(p).String())
c.Exchange, FormatCurrency(enabledPairs[i]).String())
switchedToRest = false
}
if e.Cfg.SyncTicker {
@@ -371,7 +372,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
c.Ticker.IsUsingREST = true
log.Warnf(log.SyncMgr,
"%s %s: No ticker update after 10 seconds, switching from websocket to rest\n",
c.Exchange, FormatCurrency(p).String())
c.Exchange, FormatCurrency(enabledPairs[i]).String())
switchedToRest = true
e.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTicker, false)
}
@@ -521,14 +522,15 @@ func (e *ExchangeCurrencyPairSyncer) Start() {
}
for y := range assetTypes {
for _, p := range Bot.Exchanges[x].GetEnabledPairs(assetTypes[y]) {
if e.exists(exchangeName, p, assetTypes[y]) {
enabledPairs := Bot.Exchanges[x].GetEnabledPairs(assetTypes[y])
for i := range enabledPairs {
if e.exists(exchangeName, enabledPairs[i], assetTypes[y]) {
continue
}
c := CurrencyPairSyncAgent{
AssetType: assetTypes[y],
Exchange: exchangeName,
Pair: p,
Pair: enabledPairs[i],
}
if e.Cfg.SyncTicker {

View File

@@ -2,6 +2,8 @@ package alphapoint
import (
"encoding/json"
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -17,45 +19,37 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
t.Parallel()
SetDefaults := Alphapoint{}
var a Alphapoint
SetDefaults.SetDefaults()
if SetDefaults.API.Endpoints.URL != "https://sim3.alphapoint.com:8400" {
t.Error("SetDefaults: String Incorrect -", SetDefaults.API.Endpoints.URL)
}
if SetDefaults.API.Endpoints.WebsocketURL != "wss://sim3.alphapoint.com:8401/v1/GetTicker/" {
t.Error("SetDefaults: String Incorrect -", SetDefaults.API.Endpoints.WebsocketURL)
}
}
func testSetAPIKey(a *Alphapoint) {
func TestMain(m *testing.M) {
a.SetDefaults()
a.API.Credentials.Key = apiKey
a.API.Credentials.Secret = apiSecret
a.API.AuthenticatedSupport = true
}
func testIsAPIKeysSet(a *Alphapoint) bool {
if apiKey != "" && apiSecret != "" && a.API.AuthenticatedSupport {
return true
if a.API.Endpoints.URL != "https://sim3.alphapoint.com:8400" {
log.Fatal("SetDefaults: String Incorrect -", a.API.Endpoints.URL)
}
return false
if a.API.Endpoints.WebsocketURL != "wss://sim3.alphapoint.com:8401/v1/GetTicker/" {
log.Fatal("SetDefaults: String Incorrect -", a.API.Endpoints.WebsocketURL)
}
os.Exit(m.Run())
}
func TestGetTicker(t *testing.T) {
alpha := Alphapoint{}
alpha.SetDefaults()
func areTestAPIKeysSet() bool {
return a.ValidateAPICredentials()
}
func TestGetTicker(t *testing.T) {
t.Parallel()
var ticker Ticker
var err error
if onlineTest {
ticker, err = alpha.GetTicker("BTCUSD")
ticker, err = a.GetTicker("BTCUSD")
if err != nil {
t.Fatal("Alphapoint GetTicker init error: ", err)
}
_, err = alpha.GetTicker("wigwham")
_, err = a.GetTicker("wigwham")
if err == nil {
t.Error("Alphapoint GetTicker Expected error")
}
@@ -80,19 +74,16 @@ func TestGetTicker(t *testing.T) {
}
func TestGetTrades(t *testing.T) {
alpha := Alphapoint{}
alpha.SetDefaults()
t.Parallel()
var trades Trades
var err error
if onlineTest {
trades, err = alpha.GetTrades("BTCUSD", 0, 10)
trades, err = a.GetTrades("BTCUSD", 0, 10)
if err != nil {
t.Fatalf("Init error: %s", err)
}
_, err = alpha.GetTrades("wigwham", 0, 10)
_, err = a.GetTrades("wigwham", 0, 10)
if err == nil {
t.Fatal("GetTrades Expected error")
}
@@ -121,18 +112,15 @@ func TestGetTrades(t *testing.T) {
}
func TestGetTradesByDate(t *testing.T) {
alpha := Alphapoint{}
alpha.SetDefaults()
t.Parallel()
var trades Trades
var err error
if onlineTest {
trades, err = alpha.GetTradesByDate("BTCUSD", 1414799400, 1414800000)
trades, err = a.GetTradesByDate("BTCUSD", 1414799400, 1414800000)
if err != nil {
t.Errorf("Init error: %s", err)
}
_, err = alpha.GetTradesByDate("wigwham", 1414799400, 1414800000)
_, err = a.GetTradesByDate("wigwham", 1414799400, 1414800000)
if err == nil {
t.Error("GetTradesByDate Expected error")
}
@@ -168,19 +156,16 @@ func TestGetTradesByDate(t *testing.T) {
}
func TestGetOrderbook(t *testing.T) {
alpha := Alphapoint{}
alpha.SetDefaults()
t.Parallel()
var orderBook Orderbook
var err error
if onlineTest {
orderBook, err = alpha.GetOrderbook("BTCUSD")
orderBook, err = a.GetOrderbook("BTCUSD")
if err != nil {
t.Errorf("Init error: %s", err)
}
_, err = alpha.GetOrderbook("wigwham")
_, err = a.GetOrderbook("wigwham")
if err == nil {
t.Error("GetOrderbook() Expected error")
}
@@ -213,14 +198,12 @@ func TestGetOrderbook(t *testing.T) {
}
func TestGetProductPairs(t *testing.T) {
alpha := Alphapoint{}
alpha.SetDefaults()
t.Parallel()
var products ProductPairs
var err error
if onlineTest {
products, err = alpha.GetProductPairs()
products, err = a.GetProductPairs()
if err != nil {
t.Errorf("Init error: %s", err)
}
@@ -253,14 +236,12 @@ func TestGetProductPairs(t *testing.T) {
}
func TestGetProducts(t *testing.T) {
alpha := Alphapoint{}
alpha.SetDefaults()
t.Parallel()
var products Products
var err error
if onlineTest {
products, err = alpha.GetProducts()
products, err = a.GetProducts()
if err != nil {
t.Errorf("Init error: %s", err)
}
@@ -293,12 +274,9 @@ func TestGetProducts(t *testing.T) {
}
func TestCreateAccount(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
err := a.CreateAccount("test", "account", "something@something.com", "0292383745", "lolcat123")
@@ -316,12 +294,9 @@ func TestCreateAccount(t *testing.T) {
}
func TestGetUserInfo(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.GetUserInfo()
@@ -331,12 +306,9 @@ func TestGetUserInfo(t *testing.T) {
}
func TestSetUserInfo(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.SetUserInfo("bla", "bla", "1", "meh", true, true)
@@ -346,12 +318,9 @@ func TestSetUserInfo(t *testing.T) {
}
func TestGetAccountInfo(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.GetAccountInfo()
@@ -361,12 +330,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestGetAccountTrades(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.GetAccountTrades("", 1, 2)
@@ -376,12 +342,9 @@ func TestGetAccountTrades(t *testing.T) {
}
func TestGetDepositAddresses(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.GetDepositAddresses()
@@ -391,12 +354,9 @@ func TestGetDepositAddresses(t *testing.T) {
}
func TestWithdrawCoins(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
err := a.WithdrawCoins("", "", "", 0.01)
@@ -406,12 +366,9 @@ func TestWithdrawCoins(t *testing.T) {
}
func TestCreateOrder(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.CreateOrder("", "", order.Limit.String(), 0.01, 0)
@@ -421,12 +378,9 @@ func TestCreateOrder(t *testing.T) {
}
func TestModifyExistingOrder(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.ModifyExistingOrder("", 1, 1)
@@ -436,12 +390,9 @@ func TestModifyExistingOrder(t *testing.T) {
}
func TestCancelAllExistingOrders(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
err := a.CancelAllExistingOrders("")
@@ -451,12 +402,9 @@ func TestCancelAllExistingOrders(t *testing.T) {
}
func TestGetOrders(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.GetOrders()
@@ -466,12 +414,9 @@ func TestGetOrders(t *testing.T) {
}
func TestGetOrderFee(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
testSetAPIKey(a)
if !testIsAPIKeysSet(a) {
return
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
_, err := a.GetOrderFee("", "", 1, 1)
@@ -481,45 +426,38 @@ func TestGetOrderFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := a.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
_, err := a.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet(a) && err != nil {
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet(a) && err == nil {
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
func TestGetOrderHistory(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
_, err := a.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet(a) && err != nil {
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet(a) && err == nil {
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
@@ -527,15 +465,9 @@ func TestGetOrderHistory(t *testing.T) {
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet(a *Alphapoint) bool {
return a.ValidateAPICredentials()
}
func TestSubmitOrder(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
if areTestAPIKeysSet(a) && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -553,10 +485,10 @@ func TestSubmitOrder(t *testing.T) {
}
response, err := a.SubmitOrder(orderSubmission)
if !areTestAPIKeysSet(a) && err == nil {
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet(a) && err != nil {
if areTestAPIKeysSet() && err != nil {
t.Errorf("Withdraw failed to be placed: %v", err)
if !response.IsOrderPlaced {
@@ -566,15 +498,12 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
if areTestAPIKeysSet(a) && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.BTC, currency.LTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -583,24 +512,21 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := a.CancelOrder(orderCancellation)
if !areTestAPIKeysSet(a) && err == nil {
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet(a) && err != nil {
if areTestAPIKeysSet() && err != nil {
t.Errorf("Withdraw failed to be placed: %v", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
if areTestAPIKeysSet(a) && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.BTC, currency.LTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -609,11 +535,10 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := a.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet(a) && err == nil {
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet(a) && err != nil {
if areTestAPIKeysSet() && err != nil {
t.Errorf("Withdraw failed to be placed: %v", err)
}
@@ -623,9 +548,10 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := a.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -633,10 +559,8 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
t.Parallel()
var withdrawCryptoRequest = exchange.CryptoWithdrawRequest{}
_, err := a.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if err != common.ErrNotYetImplemented {
t.Errorf("Expected 'Not implemented', received %v", err)
@@ -644,10 +568,8 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
if areTestAPIKeysSet(a) && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -659,10 +581,8 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
a := &Alphapoint{}
a.SetDefaults()
if areTestAPIKeysSet(a) && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}

View File

@@ -212,16 +212,18 @@ func (a *Alphapoint) SubmitOrder(s *order.Submit) (order.SubmitResponse, error)
s.OrderSide.String(),
s.Amount,
s.Price)
if err != nil {
return submitOrderResponse, err
}
if response > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -86,7 +86,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
a.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -171,9 +171,7 @@ func TestFormatWithdrawPermissions(t *testing.T) {
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " +
exchange.WithdrawCryptoWithEmailText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := a.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
@@ -254,7 +252,6 @@ func TestCancelExchangeOrder(t *testing.T) {
}
currencyPair := currency.NewPair(currency.BTC, currency.LTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -280,7 +277,6 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
currencyPair := currency.NewPair(currency.BTC, currency.LTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",

View File

@@ -2,6 +2,7 @@ package anx
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
@@ -346,16 +347,18 @@ func (a *ANX) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
false,
"",
false)
if err != nil {
return submitOrderResponse, err
}
if response != "" {
submitOrderResponse.OrderID = response
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -391,9 +394,9 @@ func (a *ANX) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error)
return cancelAllOrdersResponse, err
}
for _, order := range resp.OrderCancellationResponses {
if order.Error != CancelRequestSubmitted {
cancelAllOrdersResponse.Status[order.UUID] = order.Error
for i := range resp.OrderCancellationResponses {
if resp.OrderCancellationResponses[i].Error != CancelRequestSubmitted {
cancelAllOrdersResponse.Status[resp.OrderCancellationResponses[i].UUID] = resp.OrderCancellationResponses[i].Error
}
}
@@ -414,7 +417,7 @@ func (a *ANX) GetDepositAddress(cryptocurrency currency.Code, _ string) (string,
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (a *ANX) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.CryptoWithdrawRequest) (string, error) {
return a.Send(withdrawRequest.Currency.String(), withdrawRequest.Address, "", fmt.Sprintf("%v", withdrawRequest.Amount))
return a.Send(withdrawRequest.Currency.String(), withdrawRequest.Address, "", strconv.FormatFloat(withdrawRequest.Amount, 'f', -1, 64))
}
// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is

View File

@@ -194,7 +194,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -282,7 +282,6 @@ func TestFormatWithdrawPermissions(t *testing.T) {
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := b.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
@@ -382,7 +381,6 @@ func TestCancelExchangeOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -407,7 +405,6 @@ func TestCancelAllExchangeOrders(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",

View File

@@ -53,8 +53,9 @@ func (b *Binance) WsConnect() error {
kline +
"/" +
depth
for _, ePair := range b.GetEnabledPairs(asset.Spot) {
err = b.SeedLocalCache(ePair)
enabledPairs := b.GetEnabledPairs(asset.Spot)
for i := range enabledPairs {
err = b.SeedLocalCache(enabledPairs[i])
if err != nil {
return err
}

View File

@@ -426,16 +426,18 @@ func (b *Binance) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
}
response, err := b.NewOrder(&orderRequest)
if err != nil {
return submitOrderResponse, err
}
if response.OrderID > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
if response.ExecutedQty == response.OrigQty {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -85,8 +85,9 @@ const (
// depending on some factors (e.g. servers load, endpoint, etc.).
type Bitfinex struct {
exchange.Base
WebsocketConn *wshandler.WebsocketConnection
WebsocketSubdChannels map[int]WebsocketChanInfo
WebsocketConn *wshandler.WebsocketConnection
AuthenticatedWebsocketConn *wshandler.WebsocketConnection
WebsocketSubdChannels map[int]WebsocketChanInfo
}
// GetPlatformStatus returns the Bifinex platform status
@@ -358,11 +359,11 @@ func (b *Bitfinex) GetTradesV2(currencyPair string, timestampStart, timestampEnd
}
var tempHistory TradeStructureV2
for _, data := range resp {
tempHistory.TID = int64(data[0].(float64))
tempHistory.Timestamp = int64(data[1].(float64))
tempHistory.Amount = data[2].(float64)
tempHistory.Price = data[3].(float64)
for i := range resp {
tempHistory.TID = int64(resp[i][0].(float64))
tempHistory.Timestamp = int64(resp[i][1].(float64))
tempHistory.Amount = resp[i][2].(float64)
tempHistory.Price = resp[i][3].(float64)
tempHistory.Exchange = b.Name
tempHistory.Type = order.Buy.String()

View File

@@ -1,8 +1,10 @@
package bitfinex
import (
"log"
"net/http"
"net/url"
"os"
"reflect"
"testing"
"time"
@@ -25,37 +27,43 @@ const (
)
var b Bitfinex
var wsAuthExecuted bool
func TestSetup(t *testing.T) {
func TestMain(m *testing.M) {
b.SetDefaults()
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Bitfinex load config error", err)
log.Fatal("Bitfinex load config error", err)
}
bfxConfig, err := cfg.GetExchangeConfig("Bitfinex")
if err != nil {
t.Error("Bitfinex Setup() init error")
log.Fatal("Bitfinex Setup() init error")
}
err = b.Setup(bfxConfig)
if err != nil {
t.Fatal("Bitfinex setup error", err)
log.Fatal("Bitfinex setup error", err)
}
b.API.Credentials.Key = apiKey
b.API.Credentials.Secret = apiSecret
if !b.Enabled || b.API.AuthenticatedSupport ||
b.Verbose || b.Websocket.IsEnabled() || len(b.BaseCurrencies) < 1 {
t.Error("Bitfinex Setup values not set correctly")
log.Fatal("Bitfinex Setup values not set correctly")
}
if areTestAPIKeysSet() {
b.API.AuthenticatedSupport = true
b.API.AuthenticatedWebsocketSupport = true
}
b.API.AuthenticatedSupport = true
b.API.AuthenticatedWebsocketSupport = true
// custom rate limit for testing
b.Requester.SetRateLimit(true, time.Millisecond*300, 1)
b.Requester.SetRateLimit(false, time.Millisecond*300, 1)
os.Exit(m.Run())
}
func TestAppendOptionalDelimiter(t *testing.T) {
t.Parallel()
curr1 := currency.NewPairFromString("BTCUSD")
b.appendOptionalDelimiter(&curr1)
if curr1.Delimiter != "" {
@@ -71,7 +79,6 @@ func TestAppendOptionalDelimiter(t *testing.T) {
func TestGetPlatformStatus(t *testing.T) {
t.Parallel()
result, err := b.GetPlatformStatus()
if err != nil {
t.Errorf("TestGetPlatformStatus error: %s", err)
@@ -653,7 +660,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -665,11 +672,10 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
t.Parallel()
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() {
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0.002) || err != nil {
t.Error(err)
@@ -738,20 +744,16 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawFiatWithAPIPermissionText
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 = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -765,9 +767,7 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -787,9 +787,7 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -806,23 +804,25 @@ func TestSubmitOrder(t *testing.T) {
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 {
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
}
if areTestAPIKeysSet() && !response.IsOrderPlaced {
t.Error("Order not placed")
}
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -840,15 +840,12 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrdera(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -871,6 +868,10 @@ func TestCancelAllExchangeOrdera(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := b.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -878,8 +879,7 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -903,9 +903,7 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -938,9 +936,7 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -979,6 +975,7 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() {
_, err := b.GetDepositAddress(currency.BTC, "deposit")
if err != nil {
@@ -992,40 +989,168 @@ func TestGetDepositAddress(t *testing.T) {
}
}
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
b.WebsocketConn = &wshandler.WebsocketConnection{
func setupWs() {
b.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: b.Name,
URL: b.Websocket.GetWebsocketURL(),
URL: authenticatedBitfinexWebsocketEndpoint,
Verbose: b.Verbose,
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
}
var dialer websocket.Dialer
err := b.WebsocketConn.Dial(&dialer, http.Header{})
err := b.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{})
if err != nil {
t.Fatal(err)
log.Fatal(err)
}
b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
go b.WsReadData(b.AuthenticatedWebsocketConn)
go b.WsDataHandler()
err = b.WsSendAuth()
}
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
runAuth(t)
}
func runAuth(t *testing.T) {
setupWs()
err := b.WsSendAuth()
if err != nil {
t.Error(err)
}
timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout)
select {
case resp := <-b.Websocket.DataHandler:
if resp.(map[string]interface{})["event"] != "auth" && resp.(map[string]interface{})["status"] != "OK" {
t.Error("expected successful login")
if logResponse, ok := resp.(map[string]interface{}); ok {
if logResponse["event"] != "auth" && logResponse["status"] != "OK" {
t.Error("expected successful login")
}
} else {
t.Error("Unexpected response")
}
case <-timer.C:
t.Error("Have not received a response")
}
timer.Stop()
wsAuthExecuted = true
}
// TestWsPlaceOrder dials websocket, sends order request.
func TestWsPlaceOrder(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
if !wsAuthExecuted {
runAuth(t)
}
_, err := b.WsNewOrder(&WsNewOrderRequest{
CustomID: 1337,
Type: order.Buy.String(),
Symbol: "tBTCUSD",
Amount: 10,
Price: -10,
})
if err != nil {
t.Error(err)
}
}
// TestWsCancelOrder dials websocket, sends cancel request.
func TestWsCancelOrder(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
if !wsAuthExecuted {
runAuth(t)
}
err := b.WsCancelOrder(1234)
if err != nil {
t.Error(err)
}
}
// TestWsCancelOrder dials websocket, sends modify request.
func TestWsUpdateOrder(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
if !wsAuthExecuted {
runAuth(t)
}
err := b.WsModifyOrder(&WsUpdateOrderRequest{
OrderID: 1234,
Price: -111,
Amount: 111,
})
if err != nil {
t.Error(err)
}
}
// TestWsCancelAllOrders dials websocket, sends cancel all request.
func TestWsCancelAllOrders(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
if !wsAuthExecuted {
runAuth(t)
}
err := b.WsCancelAllOrders()
if err != nil {
t.Error(err)
}
}
// TestWsCancelAllOrders dials websocket, sends cancel all request.
func TestWsCancelMultiOrders(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
if !wsAuthExecuted {
runAuth(t)
}
err := b.WsCancelMultiOrders([]int64{1, 2, 3, 4})
if err != nil {
t.Error(err)
}
}
// TestWsNewOffer dials websocket, sends new offer request.
func TestWsNewOffer(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
if !wsAuthExecuted {
runAuth(t)
}
err := b.WsNewOffer(&WsNewOfferRequest{
Type: order.Limit.String(),
Symbol: "fBTC",
Amount: -10,
Rate: 10,
Period: 30,
})
if err != nil {
t.Error(err)
}
time.Sleep(time.Second)
}
// TestWsCancelOffer dials websocket, sends cancel offer request.
func TestWsCancelOffer(t *testing.T) {
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip("API keys not set, skipping")
}
if !wsAuthExecuted {
runAuth(t)
}
err := b.WsCancelOffer(1234)
if err != nil {
t.Error(err)
}
time.Sleep(time.Second)
}

View File

@@ -385,7 +385,7 @@ type WebsocketChanInfo struct {
// WebsocketBook holds booking information
type WebsocketBook struct {
Price float64
Count int
ID int
Amount float64
}
@@ -397,6 +397,16 @@ type WebsocketTrade struct {
Amount float64
}
// WebsocketCandle candle data
type WebsocketCandle struct {
Timestamp int64
Open float64
Close float64
High float64
Low float64
Volume float64
}
// WebsocketTicker holds ticker information
type WebsocketTicker struct {
Bid float64
@@ -416,7 +426,11 @@ type WebsocketPosition struct {
Amount float64
Price float64
MarginFunding float64
MarginFundingType int
MarginFundingType int64
ProfitLoss float64
ProfitLossPercent float64
LiquidationPrice float64
Leverage float64
}
// WebsocketWallet holds wallet information
@@ -459,6 +473,9 @@ type WebsocketTradeData struct {
OrderID int64
AmountExecuted float64
PriceExecuted float64
OrderType string
OrderPrice float64
Maker bool
Fee float64
FeeCurrency string
}
@@ -468,21 +485,215 @@ type ErrorCapture struct {
Message string `json:"message"`
}
// TimeInterval represents interval enum.
type TimeInterval string
// WebsocketHandshake defines the communication between the websocket API for
// initial connection
type WebsocketHandshake struct {
Event string `json:"event"`
Code int64 `json:"code"`
Version float64 `json:"version"`
}
// TimeInvterval vars
var (
TimeIntervalMinute = TimeInterval("1m")
TimeIntervalFiveMinutes = TimeInterval("5m")
TimeIntervalFifteenMinutes = TimeInterval("15m")
TimeIntervalThirtyMinutes = TimeInterval("30m")
TimeIntervalHour = TimeInterval("1h")
TimeIntervalThreeHours = TimeInterval("3h")
TimeIntervalSixHours = TimeInterval("6h")
TimeIntervalTwelveHours = TimeInterval("12h")
TimeIntervalDay = TimeInterval("1d")
TimeIntervalSevenDays = TimeInterval("7d")
TimeIntervalFourteenDays = TimeInterval("14d")
TimeIntervalMonth = TimeInterval("1M")
const (
authenticatedBitfinexWebsocketEndpoint = "wss://api.bitfinex.com/ws/2"
publicBitfinexWebsocketEndpoint = "wss://api-pub.bitfinex.com/ws/2"
pong = "pong"
wsHeartbeat = "hb"
wsPositionSnapshot = "ps"
wsPositionNew = "pn"
wsPositionUpdate = "pu"
wsPositionClose = "pc"
wsWalletSnapshot = "ws"
wsWalletUpdate = "wu"
wsTradeExecutionUpdate = "tu"
wsTradeExecuted = "te"
wsFundingCreditSnapshot = "fcs"
wsFundingCreditNew = "fcn"
wsFundingCreditUpdate = "fcu"
wsFundingCreditCancel = "fcc"
wsFundingLoanSnapshot = "fls"
wsFundingLoanNew = "fln"
wsFundingLoanUpdate = "flu"
wsFundingLoanCancel = "flc"
wsFundingTradeExecuted = "fte"
wsFundingTradeUpdate = "ftu"
wsFundingInfoUpdate = "fiu"
wsBalanceUpdate = "bu"
wsMarginInfoUpdate = "miu"
wsNotification = "n"
wsOrderNew = "on"
wsOrderUpdate = "ou"
wsOrderCancel = "oc"
wsFundingOrderSnapshot = "fos"
wsFundingOrderNew = "fon"
wsFundingOrderUpdate = "fou"
wsFundingOrderCancel = "foc"
wsCancelMultipleOrders = "oc_multi"
wsBook = "book"
wsCandles = "candles"
wsTicker = "ticker"
wsTrades = "trades"
wsError = "error"
)
// WsAuthRequest container for WS auth request
type WsAuthRequest struct {
Event string `json:"event"`
APIKey string `json:"apiKey"`
AuthPayload string `json:"authPayload"`
AuthSig string `json:"authSig"`
AuthNonce string `json:"authNonce"`
DeadManSwitch int64 `json:"dms,omitempty"`
}
// WsFundingOffer funding offer received via websocket
type WsFundingOffer struct {
ID int64
Symbol string
Created int64
Updated int64
Amount float64
AmountOrig float64
Type string
Flags interface{}
Status string
Rate float64
Period int64
Notify bool
Hidden bool
Insure bool
Renew bool
RateReal float64
}
// WsCredit credit details received via websocket
type WsCredit struct {
ID int64
Symbol string
Side string
Created int64
Updated int64
Amount float64
Flags interface{}
Status string
Rate float64
Period int64
Opened int64
LastPayout int64
Notify bool
Hidden bool
Insure bool
Renew bool
RateReal float64
NoClose bool
PositionPair string
}
// WsWallet wallet update details received via websocket
type WsWallet struct {
Type string
Currency string
Balance float64
UnsettledInterest float64
BalanceAvailable float64
}
// WsBalanceInfo the total and net assets in your account received via websocket
type WsBalanceInfo struct {
TotalAssetsUnderManagement float64
NetAssetsUnderManagement float64
}
// WsFundingInfo account funding info received via websocket
type WsFundingInfo struct {
Symbol string
YieldLoan float64
YieldLend float64
DurationLoan float64
DurationLend float64
}
// WsMarginInfoBase account margin info received via websocket
type WsMarginInfoBase struct {
UserProfitLoss float64
UserSwaps float64
MarginBalance float64
MarginNet float64
}
// WsMarginInfoBase recent funding trades received via websocket
type WsFundingTrade struct {
ID int64
Symbol string
MTSCreated int64
OfferID int64
Amount float64
Rate float64
Period int64
Maker bool
}
// WsNewOrderRequest new order request...
type WsNewOrderRequest struct {
GroupID int64 `json:"gid,omitempty"`
CustomID int64 `json:"cid,omitempty"`
Type string `json:"type"`
Symbol string `json:"symbol"`
Amount float64 `json:"amount,string"`
Price float64 `json:"price,string"`
Leverage int64 `json:"lev,omitempty"`
TrailingPrice float64 `json:"price_trailing,string,omitempty"`
AuxiliaryLimitPrice float64 `json:"price_aux_limit,string,omitempty"`
StopPrice float64 `json:"price_oco_stop,string,omitempty"`
Flags int64 `json:"flags,omitempty"`
TimeInForce string `json:"tif,omitempty"`
}
// WsUpdateOrderRequest update order request...
type WsUpdateOrderRequest struct {
OrderID int64 `json:"id,omitempty"`
CustomID int64 `json:"cid,omitempty"`
CustomIDDate string `json:"cid_date,omitempty"`
GroupID int64 `json:"gid,omitempty"`
Price float64 `json:"price,string,omitempty"`
Amount float64 `json:"amount,string,omitempty"`
Leverage int64 `json:"lev,omitempty"`
Delta float64 `json:"delta,string,omitempty"`
AuxiliaryLimitPrice float64 `json:"price_aux_limit,string,omitempty"`
TrailingPrice float64 `json:"price_trailing,string,omitempty"`
Flags int64 `json:"flags,omitempty"`
TimeInForce string `json:"tif,omitempty"`
}
// WsCancelOrderRequest cancel order request...
type WsCancelOrderRequest struct {
OrderID int64 `json:"id,omitempty"`
CustomID int64 `json:"cid,omitempty"`
CustomIDDate string `json:"cid_date,omitempty"`
}
// WsCancelGroupOrdersRequest cancel orders request...
type WsCancelGroupOrdersRequest struct {
OrderID []int64 `json:"id,omitempty"`
CustomID [][]int64 `json:"cid,omitempty"`
GroupOrderID []int64 `json:"gid,omitempty"`
}
// WsNewOfferRequest new offer request
type WsNewOfferRequest struct {
Type string `json:"type,omitempty"`
Symbol string `json:"symbol,omitempty"`
Amount float64 `json:"amount,string,omitempty"`
Rate float64 `json:"rate,string,omitempty"`
Period float64 `json:"period,omitempty"`
Flags int64 `json:"flags,omitempty"`
}
// WsCancelOfferRequest cancel offer request
type WsCancelOfferRequest struct {
OrderID int64 `json:"id"`
}
// WsCancelAllOrdersRequest cancel all orders request
type WsCancelAllOrdersRequest struct {
All int64 `json:"all"`
}

File diff suppressed because it is too large Load Diff

View File

@@ -87,7 +87,6 @@ func (b *Bitfinex) SetDefaults() {
CancelOrder: true,
SubmitOrder: true,
SubmitOrders: true,
ModifyOrder: true,
DepositHistory: true,
WithdrawalHistory: true,
TradeFetching: true,
@@ -99,12 +98,21 @@ func (b *Bitfinex) SetDefaults() {
CryptoWithdrawalFee: true,
},
WebsocketCapabilities: protocol.Features{
AccountBalance: true,
CancelOrders: true,
CancelOrder: true,
SubmitOrder: true,
ModifyOrder: true,
TickerFetching: true,
KlineFetching: true,
TradeFetching: true,
OrderbookFetching: true,
AccountInfo: true,
Subscribe: true,
Unsubscribe: true,
AuthenticatedEndpoints: true,
MessageCorrelation: true,
DeadMansSwitch: true,
},
WithdrawPermissions: exchange.AutoWithdrawCryptoWithAPIPermission |
exchange.AutoWithdrawFiatWithAPIPermission,
@@ -121,7 +129,7 @@ func (b *Bitfinex) SetDefaults() {
b.API.Endpoints.URLDefault = bitfinexAPIURLBase
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints.WebsocketURL = bitfinexWebsocket
b.API.Endpoints.WebsocketURL = publicBitfinexWebsocketEndpoint
b.Websocket = wshandler.New()
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -146,7 +154,7 @@ func (b *Bitfinex) Setup(exch *config.ExchangeConfig) error {
Verbose: exch.Verbose,
AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport,
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: bitfinexWebsocket,
DefaultURL: publicBitfinexWebsocketEndpoint,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
Connector: b.WsConnect,
@@ -166,6 +174,14 @@ func (b *Bitfinex) Setup(exch *config.ExchangeConfig) error {
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
b.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: b.Name,
URL: authenticatedBitfinexWebsocketEndpoint,
ProxyURL: b.Websocket.GetProxyAddress(),
Verbose: b.Verbose,
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
b.Websocket.Orderbook.Setup(
exch.WebsocketOrderbookBufferLimit,
@@ -318,6 +334,7 @@ func (b *Bitfinex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (order
func (b *Bitfinex) GetAccountInfo() (exchange.AccountInfo, error) {
var response exchange.AccountInfo
response.Exchange = b.Name
accountBalance, err := b.GetAccountBalance()
if err != nil {
return response, err
@@ -358,39 +375,67 @@ func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]
}
// SubmitOrder submits a new order
func (b *Bitfinex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
func (b *Bitfinex) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
err := o.Validate()
if err != nil {
return submitOrderResponse, err
}
if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
submitOrderResponse.OrderID, err = b.WsNewOrder(&WsNewOrderRequest{
CustomID: b.AuthenticatedWebsocketConn.GenerateMessageID(false),
Type: o.OrderType.String(),
Symbol: b.FormatExchangeCurrency(o.Pair, asset.Spot).String(),
Amount: o.Amount,
Price: o.Price,
})
if err != nil {
return submitOrderResponse, err
}
} else {
var response Order
isBuying := o.OrderSide == order.Buy
b.appendOptionalDelimiter(&o.Pair)
response, err = b.NewOrder(o.Pair.String(),
o.Amount,
o.Price,
isBuying,
o.OrderType.String(),
false)
if err != nil {
return submitOrderResponse, err
}
if response.OrderID > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10)
}
if response.RemainingAmount == 0 {
submitOrderResponse.FullyMatched = true
}
var isBuying bool
if s.OrderSide == order.Buy {
isBuying = true
}
b.appendOptionalDelimiter(&s.Pair)
response, err := b.NewOrder(s.Pair.String(),
s.Amount,
s.Price,
isBuying,
s.OrderType.String(),
false)
if response.OrderID > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
}
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bitfinex) ModifyOrder(action *order.Modify) (string, error) {
return "", common.ErrFunctionNotSupported
orderIDInt, err := strconv.ParseInt(action.OrderID, 10, 64)
if err != nil {
return action.OrderID, err
}
if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
if action.Side == order.Sell && action.Amount > 0 {
action.Amount = -1 * action.Amount
}
err = b.WsModifyOrder(&WsUpdateOrderRequest{
OrderID: orderIDInt,
Price: action.Price,
Amount: action.Amount,
})
return action.OrderID, err
}
return "", common.ErrNotYetImplemented
}
// CancelOrder cancels an order by its corresponding ID number
@@ -399,13 +444,22 @@ func (b *Bitfinex) CancelOrder(order *order.Cancel) error {
if err != nil {
return err
}
_, err = b.CancelExistingOrder(orderIDInt)
if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
err = b.WsCancelOrder(orderIDInt)
} else {
_, err = b.CancelExistingOrder(orderIDInt)
}
return err
}
// CancelAllOrders cancels all orders associated with a currency pair
func (b *Bitfinex) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
_, err := b.CancelAllExistingOrders()
var err error
if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
err = b.WsCancelAllOrders()
} else {
_, err = b.CancelAllExistingOrders()
}
return order.CancelAllResponse{}, err
}
@@ -422,7 +476,8 @@ func (b *Bitfinex) GetDepositAddress(cryptocurrency currency.Code, accountID str
return "", err
}
resp, err := b.NewDeposit(method, accountID, 0)
var resp DepositResponse
resp, err = b.NewDeposit(method, accountID, 0)
if err != nil {
return "", err
}
@@ -547,7 +602,7 @@ func (b *Bitfinex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail,
orderDetail.Status = order.UnknownStatus
}
// API docs discrepency. Example contains prefixed "exchange "
// API docs discrepancy. Example contains prefixed "exchange "
// Return type suggests “market” / “limit” / “stop” / “trailing-stop”
orderType := strings.Replace(resp[i].Type, "exchange ", "", 1)
if orderType == "trailing-stop" {

View File

@@ -1,6 +1,8 @@
package bitflyer
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -9,7 +11,6 @@ import (
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
// Please supply your own keys here for due diligence testing
@@ -21,19 +22,16 @@ const (
var b Bitflyer
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
b.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Bitflyer load config error", err)
log.Fatal("Bitflyer load config error", err)
}
bitflyerConfig, err := cfg.GetExchangeConfig("Bitflyer")
if err != nil {
t.Error("bitflyer Setup() init error")
log.Fatal("bitflyer Setup() init error")
}
bitflyerConfig.API.AuthenticatedSupport = true
@@ -42,8 +40,10 @@ func TestSetup(t *testing.T) {
err = b.Setup(bitflyerConfig)
if err != nil {
t.Fatal("Bitflyer setup error", err)
log.Fatal("Bitflyer setup error", err)
}
os.Exit(m.Run())
}
func TestGetLatestBlockCA(t *testing.T) {
@@ -85,7 +85,7 @@ func TestGetAddressInfoCA(t *testing.T) {
t.Error("Bitflyer - GetAddressInfoCA() error:", err)
}
if v.UnconfirmedBalance == 0 || v.ConfirmedBalance == 0 {
log.Warn(log.ExchangeSys, "Donation wallet is empty :( - please consider donating")
t.Log("Donation wallet is empty :( - please consider donating")
}
}
@@ -171,7 +171,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -183,11 +183,10 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() {
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Error(err)
@@ -256,20 +255,16 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawFiatText + " & " + exchange.WithdrawCryptoViaWebsiteOnlyText
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 = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -283,9 +278,7 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -303,9 +296,7 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -328,9 +319,7 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -351,9 +340,7 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -374,8 +361,11 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -385,10 +375,6 @@ func TestWithdraw(t *testing.T) {
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := b.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if err != common.ErrNotYetImplemented {
t.Errorf("Expected 'Not Yet Implemented', received %v", err)
@@ -396,6 +382,10 @@ func TestWithdraw(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := b.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -403,9 +393,7 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -419,9 +407,7 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}

View File

@@ -1,6 +1,8 @@
package bithumb
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -20,19 +22,16 @@ const (
var b Bithumb
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
b.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Bithumb load config error", err)
log.Fatal("Bithumb load config error", err)
}
bitConfig, err := cfg.GetExchangeConfig("Bithumb")
if err != nil {
t.Error("Bithumb Setup() init error")
log.Fatal("Bithumb Setup() init error")
}
bitConfig.API.AuthenticatedSupport = true
@@ -41,8 +40,10 @@ func TestSetup(t *testing.T) {
err = b.Setup(bitConfig)
if err != nil {
t.Fatal("Bithumb setup error", err)
log.Fatal("Bithumb setup error", err)
}
os.Exit(m.Run())
}
func TestGetTradablePairs(t *testing.T) {
@@ -87,7 +88,7 @@ func TestGetTransactionHistory(t *testing.T) {
func TestGetAccountBalance(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
t.Skip()
}
@@ -98,11 +99,11 @@ func TestGetAccountBalance(t *testing.T) {
}
func TestGetWalletAddress(t *testing.T) {
if apiKey == "" || apiSecret == "" {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
t.Parallel()
_, err := b.GetWalletAddress("")
if err == nil {
t.Error("Bithumb GetWalletAddress() Expected error")
@@ -167,7 +168,7 @@ func TestWithdrawCrypto(t *testing.T) {
func TestRequestKRWDepositDetails(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := b.RequestKRWDepositDetails()
@@ -213,7 +214,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -225,10 +226,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0.0025) || err != nil {
t.Error(err)
@@ -296,20 +294,16 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText
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 = order.GetOrdersRequest{
OrderType: order.AnyType,
OrderSide: order.Sell,
@@ -324,9 +318,7 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -346,9 +338,7 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -373,15 +363,12 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -399,15 +386,12 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -431,7 +415,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() {
_, err := b.GetAccountInfo()
if err != nil {
t.Error("Bithumb GetAccountInfo() error", err)
@@ -445,6 +429,7 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
curr := currency.NewPairFromString("BTCUSD")
_, err := b.ModifyOrder(&order.Modify{
OrderID: "1337",
@@ -458,8 +443,7 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -483,9 +467,7 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -519,9 +501,7 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -534,7 +514,8 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
if apiKey != "" && apiSecret != "" {
t.Parallel()
if areTestAPIKeysSet() {
_, err := b.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("GetDepositAddress() error", err)

View File

@@ -220,9 +220,9 @@ func (b *Bithumb) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderbo
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (b *Bithumb) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
currency := p.Base.String()
curr := p.Base.String()
orderbookNew, err := b.GetOrderBook(currency)
orderbookNew, err := b.GetOrderBook(curr)
if err != nil {
return orderBook, err
}
@@ -311,20 +311,25 @@ func (b *Bithumb) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
if s.OrderSide == order.Buy {
var result MarketBuy
result, err = b.MarketBuyOrder(s.Pair.Base.String(), s.Amount)
if err != nil {
return submitOrderResponse, err
}
orderID = result.OrderID
} else if s.OrderSide == order.Sell {
var result MarketSell
result, err = b.MarketSellOrder(s.Pair.Base.String(), s.Amount)
if err != nil {
return submitOrderResponse, err
}
orderID = result.OrderID
}
if orderID != "" {
submitOrderResponse.OrderID = orderID
submitOrderResponse.FullyMatched = true
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -358,12 +363,13 @@ func (b *Bithumb) CancelAllOrders(orderCancellation *order.Cancel) (order.Cancel
}
var allOrders []OrderData
for _, currency := range b.GetEnabledPairs(asset.Spot) {
currs := b.GetEnabledPairs(asset.Spot)
for i := range currs {
orders, err := b.GetOrders("",
orderCancellation.Side.String(),
"100",
"",
currency.Base.String())
currs[i].Base.String())
if err != nil {
return cancelAllOrdersResponse, err
}

View File

@@ -1,7 +1,9 @@
package bitmex
import (
"log"
"net/http"
"os"
"sync"
"testing"
"time"
@@ -25,19 +27,16 @@ const (
var b Bitmex
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
b.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Bitmex load config error", err)
log.Fatal("Bitmex load config error", err)
}
bitmexConfig, err := cfg.GetExchangeConfig("Bitmex")
if err != nil {
t.Error("Bitmex Setup() init error")
log.Fatal("Bitmex Setup() init error")
}
bitmexConfig.API.AuthenticatedSupport = true
@@ -47,8 +46,9 @@ func TestSetup(t *testing.T) {
err = b.Setup(bitmexConfig)
if err != nil {
t.Fatal("Bitmex setup error", err)
log.Fatal("Bitmex setup error", err)
}
os.Exit(m.Run())
}
func TestStart(t *testing.T) {
@@ -386,7 +386,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -398,9 +398,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0.00075) || err != nil {
@@ -469,21 +466,15 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawCryptoWith2FAText +
" & " + exchange.WithdrawCryptoWithEmailText + " & " + 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)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -497,9 +488,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC,
@@ -521,9 +509,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -548,15 +533,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "123456789012345678901234567890123456",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -574,15 +555,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "123456789012345678901234567890123456",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -605,7 +582,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestGetAccountInfo(t *testing.T) {
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() {
_, err := b.GetAccountInfo()
if err != nil {
t.Error("GetAccountInfo() error", err)
@@ -619,6 +596,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := b.ModifyOrder(&order.Modify{OrderID: "1337"})
if err == nil {
t.Error("ModifyOrder() error")
@@ -626,9 +606,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -653,9 +630,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -668,9 +642,6 @@ 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")
}
@@ -698,8 +669,6 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}

View File

@@ -432,15 +432,18 @@ func (b *Bitmex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
}
response, err := b.CreateOrder(&orderNewParams)
if err != nil {
return submitOrderResponse, err
}
if response.OrderID != "" {
submitOrderResponse.OrderID = response.OrderID
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -38,7 +38,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
@@ -319,7 +319,6 @@ func TestFormatWithdrawPermissions(t *testing.T) {
expectedResult := exchange.AutoWithdrawCryptoText +
" & " +
exchange.AutoWithdrawFiatText
withdrawPermissions := b.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s",

View File

@@ -364,16 +364,18 @@ func (b *Bitstamp) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
s.Amount,
buy,
market)
if err != nil {
return submitOrderResponse, err
}
if response.ID > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -329,7 +329,7 @@ func (b *Bittrex) Withdraw(currency, paymentID, address string, quantity float64
var id UUID
values := url.Values{}
values.Set("currency", currency)
values.Set("quantity", fmt.Sprintf("%v", quantity))
values.Set("quantity", strconv.FormatFloat(quantity, 'f', -1, 64))
values.Set("address", address)
if len(paymentID) > 0 {
values.Set("paymentid", paymentID)

View File

@@ -1,6 +1,8 @@
package bittrex
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -19,22 +21,16 @@ const (
var b Bittrex
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
b.SetDefaults()
if b.Name != "Bittrex" {
t.Error("Bittrex - SetDefaults() error")
}
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Bittrex load config error", err)
log.Fatal("Bittrex load config error", err)
}
bConfig, err := cfg.GetExchangeConfig("Bittrex")
if err != nil {
t.Error("Bittrex Setup() init error")
log.Fatal("Bittrex Setup() init error")
}
bConfig.API.Credentials.Key = apiKey
bConfig.API.Credentials.Secret = apiSecret
@@ -42,13 +38,15 @@ func TestSetup(t *testing.T) {
err = b.Setup(bConfig)
if err != nil {
t.Fatal("Bittrex setup error", err)
log.Fatal("Bittrex setup error", err)
}
if !b.IsEnabled() || !b.API.AuthenticatedSupport ||
b.Verbose || len(b.BaseCurrencies) < 1 {
t.Error("Bittrex Setup values not set correctly")
log.Fatal("Bittrex Setup values not set correctly")
}
os.Exit(m.Run())
}
func TestGetMarkets(t *testing.T) {
@@ -236,7 +234,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -248,11 +246,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0.0025) || err != nil {
t.Error(err)
@@ -320,20 +314,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + 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)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC,
@@ -351,9 +339,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -373,9 +358,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -401,15 +383,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -427,15 +405,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -458,6 +432,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := b.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -465,8 +442,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -490,9 +465,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -506,9 +478,6 @@ 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")
}

View File

@@ -329,7 +329,6 @@ func (b *Bittrex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
}
buy := s.OrderSide == order.Buy
if s.OrderType != order.Limit {
return submitOrderResponse,
errors.New("limit orders only supported on exchange")
@@ -346,16 +345,16 @@ func (b *Bittrex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
s.Amount,
s.Price)
}
if err != nil {
return submitOrderResponse, err
}
if response.Result.ID != "" {
submitOrderResponse.OrderID = response.Result.ID
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -205,7 +205,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -320,7 +320,6 @@ func TestCancelExchangeOrder(t *testing.T) {
currencyPair := currency.NewPairWithDelimiter(currency.BTC.String(),
currency.USD.String(),
"-")
var orderCancellation = &order.Cancel{
OrderID: "b334ecef-2b42-4998-b8a4-b6b14f6d2671",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -341,7 +340,6 @@ func TestCancelAllExchangeOrders(t *testing.T) {
currencyPair := currency.NewPairWithDelimiter(currency.BTC.String(),
currency.USD.String(),
"-")
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",

View File

@@ -3,6 +3,7 @@ package btse
import (
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
@@ -357,7 +358,9 @@ func (b *BTSE) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
resp.IsOrderPlaced = true
resp.OrderID = *r
}
if s.OrderType == order.Market {
resp.FullyMatched = true
}
return resp, nil
}
@@ -475,7 +478,7 @@ func (b *BTSE) GetOrderInfo(orderID string) (order.Detail, error) {
}
od.Trades = append(od.Trades, order.TradeHistory{
Timestamp: createdAt,
TID: fills[i].ID,
TID: strconv.FormatInt(fills[i].ID, 10),
Price: fills[i].Price,
Amount: fills[i].Amount,
Exchange: b.Name,
@@ -569,7 +572,7 @@ func (b *BTSE) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, err
}
openOrder.Trades = append(openOrder.Trades, order.TradeHistory{
Timestamp: createdAt,
TID: fills[i].ID,
TID: strconv.FormatInt(fills[i].ID, 10),
Price: fills[i].Price,
Amount: fills[i].Amount,
Exchange: b.Name,

View File

@@ -1,7 +1,9 @@
package coinbasepro
import (
"log"
"net/http"
"os"
"testing"
"time"
@@ -25,20 +27,18 @@ const (
testPair = "BTC-USD"
)
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
c.SetDefaults()
c.Requester.SetRateLimit(false, time.Second, 1)
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("coinbasepro load config error", err)
log.Fatal("coinbasepro load config error", err)
}
gdxConfig, err := cfg.GetExchangeConfig("CoinbasePro")
if err != nil {
t.Error("coinbasepro Setup() init error")
log.Fatal("coinbasepro Setup() init error")
}
gdxConfig.API.Credentials.Key = apiKey
gdxConfig.API.Credentials.Secret = apiSecret
@@ -47,8 +47,10 @@ func TestSetup(t *testing.T) {
gdxConfig.API.AuthenticatedWebsocketSupport = true
err = c.Setup(gdxConfig)
if err != nil {
t.Fatal("CoinbasePro setup error", err)
log.Fatal("CoinbasePro setup error", err)
}
os.Exit(m.Run())
}
func TestGetProducts(t *testing.T) {
@@ -198,7 +200,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
c.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -210,12 +212,9 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
c.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() {
// CryptocurrencyTradeFee Basic
if resp, err := c.GetFee(feeBuilder); resp != float64(0.003) || err != nil {
t.Error(err)
@@ -371,7 +370,6 @@ func TestCalculateTradingFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
c.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawFiatWithAPIPermissionText
withdrawPermissions := c.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
@@ -380,9 +378,6 @@ func TestFormatWithdrawPermissions(t *testing.T) {
}
func TestGetActiveOrders(t *testing.T) {
c.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.BTC,
@@ -398,9 +393,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
c.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.BTC,
@@ -422,9 +414,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -450,15 +439,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -476,15 +461,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -507,6 +488,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := c.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -514,8 +498,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
c.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -539,9 +521,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -564,9 +543,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -597,8 +573,6 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if !c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}

View File

@@ -3,7 +3,6 @@ package coinbasepro
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"time"
@@ -311,7 +310,7 @@ func (c *CoinbasePro) Subscribe(channelToSubscribe wshandler.WebsocketChannelSub
},
}
if channelToSubscribe.Channel == "user" || channelToSubscribe.Channel == "full" {
n := fmt.Sprintf("%v", time.Now().Unix())
n := strconv.FormatInt(time.Now().Unix(), 10)
message := n + "GET" + "/users/self/verify"
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message),
[]byte(c.API.Credentials.Secret))

View File

@@ -400,16 +400,19 @@ func (c *CoinbasePro) SubmitOrder(s *order.Submit) (order.SubmitResponse, error)
default:
err = errors.New("order type not supported")
}
if err != nil {
return submitOrderResponse, err
}
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
if response != "" {
submitOrderResponse.OrderID = response
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -509,7 +512,7 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta
var orders []order.Detail
for i := range respOrders {
currency := currency.NewPairDelimiter(respOrders[i].ProductID,
curr := currency.NewPairDelimiter(respOrders[i].ProductID,
c.GetPairFormat(asset.Spot, false).Delimiter)
orderSide := order.Side(strings.ToUpper(respOrders[i].Side))
orderType := order.Type(strings.ToUpper(respOrders[i].Type))
@@ -530,7 +533,7 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta
OrderType: orderType,
OrderDate: orderDate,
OrderSide: orderSide,
CurrencyPair: currency,
CurrencyPair: curr,
Exchange: c.Name,
})
}
@@ -545,9 +548,9 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta
// Can Limit response to specific order status
func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) {
var respOrders []GeneralizedOrderResponse
for _, currency := range req.Currencies {
for i := range req.Currencies {
resp, err := c.GetOrders([]string{"done", "settled"},
c.FormatExchangeCurrency(currency, asset.Spot).String())
c.FormatExchangeCurrency(req.Currencies[i], asset.Spot).String())
if err != nil {
return nil, err
}
@@ -556,7 +559,7 @@ func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Deta
var orders []order.Detail
for i := range respOrders {
currency := currency.NewPairDelimiter(respOrders[i].ProductID,
curr := currency.NewPairDelimiter(respOrders[i].ProductID,
c.GetPairFormat(asset.Spot, false).Delimiter)
orderSide := order.Side(strings.ToUpper(respOrders[i].Side))
orderType := order.Type(strings.ToUpper(respOrders[i].Type))
@@ -577,7 +580,7 @@ func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Deta
OrderType: orderType,
OrderDate: orderDate,
OrderSide: orderSide,
CurrencyPair: currency,
CurrencyPair: curr,
Exchange: c.Name,
})
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
)
@@ -27,8 +28,6 @@ const (
coinbeneAPIURL = "https://openapi-exchange.coinbene.com/api/exchange/"
coinbeneAuthPath = "/api/exchange/v2"
coinbeneAPIVersion = "v2"
buy = "buy"
sell = "sell"
// Public endpoints
coinbeneFetchTicker = "/market/ticker/one"
@@ -114,9 +113,9 @@ func (c *Coinbene) PlaceOrder(price, quantity float64, symbol, direction, client
params := url.Values{}
params.Set("symbol", symbol)
switch direction {
case sell:
case order.Buy.Lower():
params.Set("direction", "2")
case buy:
case order.Sell.Lower():
params.Set("direction", "1")
default:
return resp,

View File

@@ -7,6 +7,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
// Please supply your own keys here for due diligence testing
@@ -103,7 +104,7 @@ func TestPlaceOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test, either api keys or manipulaterealorders isnt set correctly")
}
_, err := c.PlaceOrder(140, 1, btcusdt, "buy", "")
_, err := c.PlaceOrder(1, 1, btcusdt, order.Buy.Lower(), "")
if err != nil {
t.Error(err)
}

View File

@@ -3,6 +3,7 @@ package coinbene
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
@@ -504,9 +505,9 @@ func (c *Coinbene) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]
for y := range tempData.OpenOrders {
tempResp.Exchange = c.Name
tempResp.CurrencyPair = getOrdersRequest.Currencies[x]
tempResp.OrderSide = buy
if tempData.OpenOrders[y].OrderType == sell {
tempResp.OrderSide = sell
tempResp.OrderSide = order.Buy
if strings.EqualFold(tempData.OpenOrders[y].OrderType, order.Sell.String()) {
tempResp.OrderSide = order.Sell
}
t, err = time.Parse(time.RFC3339, tempData.OpenOrders[y].OrderTime)
if err != nil {
@@ -556,7 +557,7 @@ func (c *Coinbene) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]
tempResp.Exchange = c.Name
tempResp.CurrencyPair = getOrdersRequest.Currencies[x]
tempResp.OrderSide = order.Buy
if tempData.Data[y].OrderType == sell {
if strings.EqualFold(tempData.Data[y].OrderType, order.Sell.String()) {
tempResp.OrderSide = order.Sell
}
t, err = time.Parse(time.RFC3339, tempData.Data[y].OrderTime)

View File

@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
@@ -105,9 +106,8 @@ func (c *COINUT) GetTrades(instrumentID int) (Trades, error) {
}
// GetUserBalance returns the full user balance
func (c *COINUT) GetUserBalance() (UserBalance, error) {
result := UserBalance{}
func (c *COINUT) GetUserBalance() (*UserBalance, error) {
var result *UserBalance
return result, c.SendHTTPRequest(coinutBalance, nil, true, &result)
}
@@ -117,26 +117,16 @@ func (c *COINUT) NewOrder(instrumentID int64, quantity, price float64, buy bool,
params := make(map[string]interface{})
params["inst_id"] = instrumentID
if price > 0 {
params["price"] = fmt.Sprintf("%v", price)
params["price"] = strconv.FormatFloat(price, 'f', -1, 64)
}
params["qty"] = fmt.Sprintf("%v", quantity)
params["qty"] = strconv.FormatFloat(quantity, 'f', -1, 64)
params["side"] = order.Buy.String()
if !buy {
params["side"] = order.Sell.String()
}
params["client_ord_id"] = orderID
err := c.SendHTTPRequest(coinutOrder, params, true, &result)
if _, ok := result.(OrderRejectResponse); ok {
return result.(OrderRejectResponse), err
}
if _, ok := result.(OrderFilledResponse); ok {
return result.(OrderFilledResponse), err
}
if _, ok := result.(OrdersBase); ok {
return result.(OrdersBase), err
}
return result, err
return result, c.SendHTTPRequest(coinutOrder, params, true, &result)
}
// NewOrders places multiple orders on the exchange
@@ -425,3 +415,77 @@ func getInternationalBankDepositFee(c currency.Code, amount float64) float64 {
return fee
}
// IsLoaded returns whether or not the instrument map has been seeded
func (i *instrumentMap) IsLoaded() bool {
i.m.Lock()
defer i.m.Unlock()
return i.Loaded
}
// Seed seeds the instrument map
func (i *instrumentMap) Seed(curr string, id int64) {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
i.Instruments = make(map[string]int64)
}
// check to see if the instrument already exists
_, ok := i.Instruments[curr]
if ok {
return
}
i.Instruments[curr] = id
i.Loaded = true
}
// LookupInstrument looks up an instrument based on an id
func (i *instrumentMap) LookupInstrument(id int64) string {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
return ""
}
for k, v := range i.Instruments {
if v == id {
return k
}
}
return ""
}
// LookupID looks up an ID based on a string
func (i *instrumentMap) LookupID(curr string) int64 {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
return 0
}
if ic, ok := i.Instruments[curr]; ok {
return ic
}
return 0
}
// GetInstrumentIDs returns a list of IDs
func (i *instrumentMap) GetInstrumentIDs() []int64 {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
return nil
}
var instruments []int64
for _, x := range i.Instruments {
instruments = append(instruments, x)
}
return instruments
}

View File

@@ -1,7 +1,9 @@
package coinut
import (
"log"
"net/http"
"os"
"testing"
"github.com/gorilla/websocket"
@@ -24,45 +26,42 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
c.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Coinut load config error", err)
log.Fatal("Coinut load config error", err)
}
bConfig, err := cfg.GetExchangeConfig("COINUT")
if err != nil {
t.Error("Coinut Setup() init error")
log.Fatal("Coinut Setup() init error")
}
bConfig.API.AuthenticatedSupport = true
bConfig.API.AuthenticatedWebsocketSupport = true
bConfig.API.Credentials.Key = apiKey
bConfig.API.Credentials.ClientID = clientID
bConfig.Verbose = true
err = c.Setup(bConfig)
if err != nil {
t.Fatal("Coinut setup error", err)
log.Fatal("Coinut setup error", err)
}
if !c.IsEnabled() || !c.Verbose ||
c.Websocket.IsEnabled() || len(c.BaseCurrencies) < 1 {
t.Error("Coinut Setup values not set correctly")
}
c.SeedInstruments()
os.Exit(m.Run())
}
func setupWSTestAuth(t *testing.T) {
if wsSetupRan {
return
}
c.SetDefaults()
TestSetup(t)
if !c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
if areTestAPIKeysSet() {
c.Websocket.SetCanUseAuthenticatedEndpoints(true)
}
c.WebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: c.Name,
URL: coinutWebsocketURL,
@@ -71,6 +70,7 @@ func setupWSTestAuth(t *testing.T) {
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
}
var dialer websocket.Dialer
err := c.WebsocketConn.Dial(&dialer, http.Header{})
if err != nil {
@@ -84,6 +84,10 @@ func setupWSTestAuth(t *testing.T) {
t.Error(err)
}
wsSetupRan = true
_, err = c.WsGetInstruments()
if err != nil {
t.Error(err)
}
}
func TestGetInstruments(t *testing.T) {
@@ -130,12 +134,8 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
c.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := c.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
t.Error(err)
@@ -248,38 +248,29 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
c.SetDefaults()
expectedResult := exchange.WithdrawCryptoViaWebsiteOnlyText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := c.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
c.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
_, err := c.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
c.SetDefaults()
TestSetup(t)
func TestGetOrderHistoryWrapper(t *testing.T) {
setupWSTestAuth(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.BTC,
currency.LTC)},
currency.USD)},
}
_, err := c.GetOrderHistory(&getOrdersRequest)
@@ -295,9 +286,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -311,7 +299,7 @@ func TestSubmitOrder(t *testing.T) {
OrderType: order.Limit,
Price: 1,
Amount: 1,
ClientID: "meowOrder",
ClientID: "123",
}
response, err := c.SubmitOrder(orderSubmission)
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
@@ -322,15 +310,10 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
currencyPair := currency.NewPair(currency.BTC, currency.USD)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -348,15 +331,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -393,6 +372,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := c.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -400,8 +382,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
c.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -422,9 +402,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -437,9 +414,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
c.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -473,14 +447,14 @@ func TestWsAuthSubmitOrder(t *testing.T) {
if !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
order := WsSubmitOrderParameters{
ord := WsSubmitOrderParameters{
Amount: 1,
Currency: currency.NewPair(currency.LTC, currency.BTC),
OrderID: 1,
Price: 1,
Side: order.Buy,
}
_, err := c.wsSubmitOrder(&order)
_, err := c.wsSubmitOrder(&ord)
if err != nil {
t.Error(err)
}
@@ -513,12 +487,13 @@ func TestWsAuthSubmitOrders(t *testing.T) {
}
// TestWsAuthCancelOrders dials websocket, cancels orders
// doesn't care about if the order cancellations fail
func TestWsAuthCancelOrders(t *testing.T) {
setupWSTestAuth(t)
if !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
order := WsCancelOrderParameters{
ord := WsCancelOrderParameters{
Currency: currency.NewPair(currency.LTC, currency.BTC),
OrderID: 1,
}
@@ -526,9 +501,28 @@ func TestWsAuthCancelOrders(t *testing.T) {
Currency: currency.NewPair(currency.LTC, currency.BTC),
OrderID: 2,
}
_, errs := c.wsCancelOrders([]WsCancelOrderParameters{order, order2})
if len(errs) > 0 {
t.Error(errs)
resp, err := c.wsCancelOrders([]WsCancelOrderParameters{ord, order2})
if err != nil {
t.Error(err)
}
if resp.Status[0] != "OK" {
t.Error("Order failed to cancel")
}
}
// TestWsAuthCancelOrders dials websocket, cancels orders
// Checks that the wrapper oversight works
func TestWsAuthCancelOrdersWrapper(t *testing.T) {
setupWSTestAuth(t)
if !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
orderDetails := order.Cancel{
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
_, err := c.CancelAllOrders(&orderDetails)
if err != nil {
t.Error(err)
}
}
@@ -538,20 +532,23 @@ func TestWsAuthCancelOrder(t *testing.T) {
if !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
order := WsCancelOrderParameters{
ord := &WsCancelOrderParameters{
Currency: currency.NewPair(currency.LTC, currency.BTC),
OrderID: 1,
}
err := c.wsCancelOrder(order)
resp, err := c.wsCancelOrder(ord)
if err != nil {
t.Error(err)
}
if len(resp.Status) >= 1 && resp.Status[0] != "OK" {
t.Errorf("Failed to cancel order")
}
}
// TestWsAuthGetOpenOrders dials websocket, retrieves open orders
func TestWsAuthGetOpenOrders(t *testing.T) {
setupWSTestAuth(t)
err := c.wsGetOpenOrders(currency.NewPair(currency.LTC, currency.BTC))
_, err := c.wsGetOpenOrders(currency.NewPair(currency.LTC, currency.BTC).String())
if err != nil {
t.Error(err)
}
@@ -573,7 +570,6 @@ func TestCurrencyMapIsLoaded(t *testing.T) {
func TestCurrencyMapSeed(t *testing.T) {
t.Parallel()
var i instrumentMap
// Test non-seeded lookups
if id := i.LookupInstrument(1234); id != "" {
t.Error("unexpected result")

View File

@@ -1,7 +1,6 @@
package coinut
import (
"strings"
"sync"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -177,7 +176,7 @@ type CancelOrdersResponse struct {
Results []struct {
OrderID int64 `json:"order_id"`
Status string `json:"status"`
InstrumentID int `json:"inst_id"`
InstrumentID int64 `json:"inst_id"`
} `json:"results"`
}
@@ -372,10 +371,10 @@ type WsTradeUpdate struct {
// WsInstrumentList defines instrument list
type WsInstrumentList struct {
Spot map[string][]WsSupportedCurrency `json:"SPOT"`
Nonce int64 `json:"nonce"`
Reply string `json:"inst_list"`
Status []interface{} `json:"status"`
Spot map[string][]InstrumentBase `json:"SPOT"`
Nonce int64 `json:"nonce,omitempty"`
Reply string `json:"inst_list,omitempty"`
Status []interface{} `json:"status,omitempty"`
}
// WsSupportedCurrency defines supported currency on the exchange
@@ -536,14 +535,15 @@ type WsOrderFilledResponse struct {
// WsOrderData ws response data
type WsOrderData struct {
ClientOrdID int64 `json:"client_ord_id"`
InstID int64 `json:"inst_id"`
OpenQty float64 `json:"open_qty,string"`
OrderID int64 `json:"order_id"`
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
Side string `json:"side"`
Timestamp int64 `json:"timestamp"`
ClientOrdID int64 `json:"client_ord_id"`
InstID int64 `json:"inst_id"`
OpenQty float64 `json:"open_qty,string"`
OrderID int64 `json:"order_id"`
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
Side string `json:"side"`
Timestamp int64 `json:"timestamp"`
Status []string `json:"status"`
}
// WsOrderFilledCommissionData ws response data
@@ -656,20 +656,20 @@ type WsNewOrderResponse struct {
// WsGetAccountBalanceResponse contains values of each currency
type WsGetAccountBalanceResponse struct {
BCH string `json:"BCH"`
BTC string `json:"BTC"`
BTG string `json:"BTG"`
CAD string `json:"CAD"`
ETC string `json:"ETC"`
ETH string `json:"ETH"`
LCH string `json:"LCH"`
LTC string `json:"LTC"`
MYR string `json:"MYR"`
SGD string `json:"SGD"`
USD string `json:"USD"`
USDT string `json:"USDT"`
XMR string `json:"XMR"`
ZEC string `json:"ZEC"`
BCH float64 `json:"BCH,string"`
BTC float64 `json:"BTC,string"`
BTG float64 `json:"BTG,string"`
CAD float64 `json:"CAD,string"`
ETC float64 `json:"ETC,string"`
ETH float64 `json:"ETH,string"`
LCH float64 `json:"LCH,string"`
LTC float64 `json:"LTC,string"`
MYR float64 `json:"MYR,string"`
SGD float64 `json:"SGD,string"`
USD float64 `json:"USD,string"`
USDT float64 `json:"USDT,string"`
XMR float64 `json:"XMR,string"`
ZEC float64 `json:"ZEC,string"`
Nonce int64 `json:"nonce"`
Reply string `json:"reply"`
Status []string `json:"status"`
@@ -681,79 +681,3 @@ type instrumentMap struct {
Loaded bool
m sync.Mutex
}
// IsLoaded returns whether or not the instrument map has been seeded
func (i *instrumentMap) IsLoaded() bool {
i.m.Lock()
defer i.m.Unlock()
return i.Loaded
}
// Seed seeds the instrument map
func (i *instrumentMap) Seed(currency string, id int64) {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
i.Instruments = make(map[string]int64)
}
// check to see if the instrument already exists
_, ok := i.Instruments[currency]
if ok {
return
}
i.Instruments[currency] = id
i.Loaded = true
}
// LookupInstrument looks up an instrument based on an id
func (i *instrumentMap) LookupInstrument(id int64) string {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
return ""
}
for k, v := range i.Instruments {
if v == id {
return k
}
}
return ""
}
// LookupID looks up an ID based on a string
func (i *instrumentMap) LookupID(currency string) int64 {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
return 0
}
for k, v := range i.Instruments {
if strings.EqualFold(currency, k) {
return v
}
}
return 0
}
// GetInstrumentIDs returns a list of IDs
func (i *instrumentMap) GetInstrumentIDs() []int64 {
i.m.Lock()
defer i.m.Unlock()
if !i.Loaded {
return nil
}
var instruments []int64
for _, x := range i.Instruments {
instruments = append(instruments, x)
}
return instruments
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
const (
@@ -25,8 +26,7 @@ const (
)
var (
channels map[string]chan []byte
wsInstrumentMap instrumentMap
channels map[string]chan []byte
)
// NOTE for speed considerations
@@ -46,13 +46,17 @@ func (c *COINUT) WsConnect() error {
}
go c.WsHandleData()
if !wsInstrumentMap.IsLoaded() {
err = c.WsSetInstrumentList()
if !c.instrumentMap.IsLoaded() {
_, err = c.WsGetInstruments()
if err != nil {
return err
}
}
c.wsAuthenticate()
err = c.wsAuthenticate()
if err != nil {
c.Websocket.SetCanUseAuthenticatedEndpoints(false)
log.Error(log.WebsocketMgr, err)
}
c.GenerateDefaultSubscriptions()
// define bi-directional communication
@@ -135,7 +139,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
return
}
currencyPair := wsInstrumentMap.LookupInstrument(ticker.InstID)
currencyPair := c.instrumentMap.LookupInstrument(ticker.InstID)
c.Websocket.DataHandler <- wshandler.TickerData{
Exchange: c.Name,
Volume: ticker.Volume24,
@@ -164,7 +168,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
c.Websocket.DataHandler <- err
return
}
currencyPair := wsInstrumentMap.LookupInstrument(orderbooksnapshot.InstID)
currencyPair := c.instrumentMap.LookupInstrument(orderbooksnapshot.InstID)
c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
Exchange: c.Name,
Asset: asset.Spot,
@@ -184,7 +188,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
c.Websocket.DataHandler <- err
return
}
currencyPair := wsInstrumentMap.LookupInstrument(orderbookUpdate.InstID)
currencyPair := c.instrumentMap.LookupInstrument(orderbookUpdate.InstID)
c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
Exchange: c.Name,
Asset: asset.Spot,
@@ -207,7 +211,7 @@ func (c *COINUT) wsProcessResponse(resp []byte) {
c.Websocket.DataHandler <- err
return
}
currencyPair := wsInstrumentMap.LookupInstrument(tradeUpdate.InstID)
currencyPair := c.instrumentMap.LookupInstrument(tradeUpdate.InstID)
c.Websocket.DataHandler <- wshandler.TradeData{
Timestamp: time.Unix(tradeUpdate.Timestamp, 0),
CurrencyPair: currency.NewPairFromFormattedPairs(currencyPair,
@@ -238,8 +242,9 @@ func (c *COINUT) GetNonce() int64 {
return int64(c.Nonce.Get())
}
// WsSetInstrumentList fetches instrument list and propagates a local cache
func (c *COINUT) WsSetInstrumentList() error {
// WsGetInstruments fetches instrument list and propagates a local cache
func (c *COINUT) WsGetInstruments() (Instruments, error) {
var list Instruments
request := wsRequest{
Request: "inst_list",
SecType: strings.ToUpper(asset.Spot.String()),
@@ -247,20 +252,19 @@ func (c *COINUT) WsSetInstrumentList() error {
}
resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request)
if err != nil {
return err
return list, err
}
var list WsInstrumentList
err = json.Unmarshal(resp, &list)
if err != nil {
return err
return list, err
}
for curr, data := range list.Spot {
wsInstrumentMap.Seed(curr, data[0].InstID)
for curr, data := range list.Instruments {
c.instrumentMap.Seed(curr, data[0].InstID)
}
if len(wsInstrumentMap.GetInstrumentIDs()) == 0 {
return errors.New("instrument list failed to populate")
if len(c.instrumentMap.GetInstrumentIDs()) == 0 {
return list, errors.New("instrument list failed to populate")
}
return nil
return list, nil
}
// WsProcessOrderbookSnapshot processes the orderbook snapshot
@@ -285,7 +289,7 @@ func (c *COINUT) WsProcessOrderbookSnapshot(ob *WsOrderbookSnapshot) error {
newOrderBook.Asks = asks
newOrderBook.Bids = bids
newOrderBook.Pair = currency.NewPairFromFormattedPairs(
wsInstrumentMap.LookupInstrument(ob.InstID),
c.instrumentMap.LookupInstrument(ob.InstID),
c.GetEnabledPairs(asset.Spot),
c.GetPairFormat(asset.Spot, true),
)
@@ -298,7 +302,7 @@ func (c *COINUT) WsProcessOrderbookSnapshot(ob *WsOrderbookSnapshot) error {
// WsProcessOrderbookUpdate process an orderbook update
func (c *COINUT) WsProcessOrderbookUpdate(update *WsOrderbookUpdate) error {
p := currency.NewPairFromFormattedPairs(
wsInstrumentMap.LookupInstrument(update.InstID),
c.instrumentMap.LookupInstrument(update.InstID),
c.GetEnabledPairs(asset.Spot),
c.GetPairFormat(asset.Spot, true),
)
@@ -335,7 +339,7 @@ func (c *COINUT) GenerateDefaultSubscriptions() {
func (c *COINUT) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
subscribe := wsRequest{
Request: channelToSubscribe.Channel,
InstID: wsInstrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency,
InstID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency,
asset.Spot).String()),
Subscribe: true,
Nonce: c.WebsocketConn.GenerateMessageID(false),
@@ -347,7 +351,7 @@ func (c *COINUT) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscrip
func (c *COINUT) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
subscribe := wsRequest{
Request: channelToSubscribe.Channel,
InstID: wsInstrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency,
InstID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency,
asset.Spot).String()),
Subscribe: false,
Nonce: c.WebsocketConn.GenerateMessageID(false),
@@ -406,7 +410,7 @@ func (c *COINUT) wsAuthenticate() error {
return nil
}
func (c *COINUT) wsGetAccountBalance() (*WsGetAccountBalanceResponse, error) {
func (c *COINUT) wsGetAccountBalance() (*UserBalance, error) {
if !c.Websocket.CanUseAuthenticatedEndpoints() {
return nil, fmt.Errorf("%v not authorised to submit order", c.Name)
}
@@ -418,7 +422,7 @@ func (c *COINUT) wsGetAccountBalance() (*WsGetAccountBalanceResponse, error) {
if err != nil {
return nil, err
}
var response WsGetAccountBalanceResponse
var response UserBalance
err = json.Unmarshal(resp, &response)
if err != nil {
return nil, err
@@ -429,21 +433,21 @@ func (c *COINUT) wsGetAccountBalance() (*WsGetAccountBalanceResponse, error) {
return &response, nil
}
func (c *COINUT) wsSubmitOrder(order *WsSubmitOrderParameters) (*WsStandardOrderResponse, error) {
func (c *COINUT) wsSubmitOrder(o *WsSubmitOrderParameters) (*WsStandardOrderResponse, error) {
if !c.Websocket.CanUseAuthenticatedEndpoints() {
return nil, fmt.Errorf("%v not authorised to submit order", c.Name)
}
curr := c.FormatExchangeCurrency(order.Currency, asset.Spot).String()
curr := c.FormatExchangeCurrency(o.Currency, asset.Spot).String()
var orderSubmissionRequest WsSubmitOrderRequest
orderSubmissionRequest.Request = "new_order"
orderSubmissionRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
orderSubmissionRequest.InstID = wsInstrumentMap.LookupID(curr)
orderSubmissionRequest.Qty = order.Amount
orderSubmissionRequest.Price = order.Price
orderSubmissionRequest.Side = string(order.Side)
orderSubmissionRequest.InstID = c.instrumentMap.LookupID(curr)
orderSubmissionRequest.Qty = o.Amount
orderSubmissionRequest.Price = o.Price
orderSubmissionRequest.Side = string(o.Side)
if order.OrderID > 0 {
orderSubmissionRequest.OrderID = order.OrderID
if o.OrderID > 0 {
orderSubmissionRequest.OrderID = o.OrderID
}
resp, err := c.WebsocketConn.SendMessageReturnResponse(orderSubmissionRequest.Nonce, orderSubmissionRequest)
if err != nil {
@@ -548,7 +552,7 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO
Qty: orders[i].Amount,
Price: orders[i].Price,
Side: string(orders[i].Side),
InstID: wsInstrumentMap.LookupID(curr),
InstID: c.instrumentMap.LookupID(curr),
ClientOrdID: i + 1,
})
}
@@ -585,7 +589,7 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO
if len(standardOrder.Reasons) > 0 && standardOrder.Reasons[0] != "" {
errors = append(errors, fmt.Errorf("%v order submission failed for currency %v and orderID %v, message %v ",
c.Name,
wsInstrumentMap.LookupInstrument(standardOrder.InstID),
c.instrumentMap.LookupInstrument(standardOrder.InstID),
standardOrder.OrderID,
standardOrder.Reasons[0]))
@@ -597,73 +601,73 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]WsStandardO
return ordersResponse, errors
}
func (c *COINUT) wsGetOpenOrders(p currency.Pair) error {
func (c *COINUT) wsGetOpenOrders(curr string) (*WsUserOpenOrdersResponse, error) {
var response *WsUserOpenOrdersResponse
if !c.Websocket.CanUseAuthenticatedEndpoints() {
return fmt.Errorf("%v not authorised to get open orders", c.Name)
return response, fmt.Errorf("%v not authorised to get open orders", c.Name)
}
curr := c.FormatExchangeCurrency(p, asset.Spot).String()
var openOrdersRequest WsGetOpenOrdersRequest
openOrdersRequest.Request = "user_open_orders"
openOrdersRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
openOrdersRequest.InstID = wsInstrumentMap.LookupID(curr)
openOrdersRequest.InstID = c.instrumentMap.LookupID(curr)
resp, err := c.WebsocketConn.SendMessageReturnResponse(openOrdersRequest.Nonce, openOrdersRequest)
if err != nil {
return err
return response, err
}
var response map[string]interface{}
err = json.Unmarshal(resp, &response)
if err != nil {
return err
return response, err
}
if response["status"].([]interface{})[0] != "OK" {
return fmt.Errorf("%v get open orders failed for currency %v",
if response.Status[0] != "OK" {
return response, fmt.Errorf("%v get open orders failed for currency %v",
c.Name,
p)
curr)
}
return nil
return response, nil
}
func (c *COINUT) wsCancelOrder(cancellation WsCancelOrderParameters) error {
func (c *COINUT) wsCancelOrder(cancellation *WsCancelOrderParameters) (*CancelOrdersResponse, error) {
var response *CancelOrdersResponse
if !c.Websocket.CanUseAuthenticatedEndpoints() {
return fmt.Errorf("%v not authorised to cancel order", c.Name)
return response, fmt.Errorf("%v not authorised to cancel order", c.Name)
}
currency := c.FormatExchangeCurrency(cancellation.Currency, asset.Spot).String()
curr := c.FormatExchangeCurrency(cancellation.Currency, asset.Spot).String()
var cancellationRequest WsCancelOrderRequest
cancellationRequest.Request = "cancel_order"
cancellationRequest.InstID = wsInstrumentMap.LookupID(currency)
cancellationRequest.InstID = c.instrumentMap.LookupID(curr)
cancellationRequest.OrderID = cancellation.OrderID
cancellationRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
resp, err := c.WebsocketConn.SendMessageReturnResponse(cancellationRequest.Nonce, cancellationRequest)
if err != nil {
return err
return response, err
}
var response map[string]interface{}
err = json.Unmarshal(resp, &response)
if err != nil {
return err
return response, err
}
if response["status"].([]interface{})[0] != "OK" {
return fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v",
if response.Status[0] != "OK" {
return response, fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v",
c.Name,
cancellation.Currency,
cancellation.OrderID,
response["status"])
response.Status[0])
}
return nil
return response, nil
}
func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*WsCancelOrdersResponse, []error) {
var errors []error
func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*CancelOrdersResponse, error) {
var err error
var response *CancelOrdersResponse
if !c.Websocket.CanUseAuthenticatedEndpoints() {
return nil, errors
return nil, err
}
cancelOrderRequest := WsCancelOrdersRequest{}
var cancelOrderRequest WsCancelOrdersRequest
for i := range cancellations {
currency := c.FormatExchangeCurrency(cancellations[i].Currency, asset.Spot).String()
curr := c.FormatExchangeCurrency(cancellations[i].Currency, asset.Spot).String()
cancelOrderRequest.Entries = append(cancelOrderRequest.Entries, WsCancelOrdersRequestEntry{
InstID: wsInstrumentMap.LookupID(currency),
InstID: c.instrumentMap.LookupID(curr),
OrderID: cancellations[i].OrderID,
})
}
@@ -672,53 +676,40 @@ func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*WsCan
cancelOrderRequest.Nonce = c.WebsocketConn.GenerateMessageID(false)
resp, err := c.WebsocketConn.SendMessageReturnResponse(cancelOrderRequest.Nonce, cancelOrderRequest)
if err != nil {
return nil, []error{err}
return response, err
}
var response WsCancelOrdersResponse
err = json.Unmarshal(resp, &response)
if err != nil {
return nil, []error{err}
return response, err
}
if response.Status[0] != "OK" {
return &response, []error{err}
}
for i := range response.Results {
if response.Results[i].Status != "OK" {
errors = append(errors, fmt.Errorf("%v order cancellation failed for currency %v and orderID %v, message %v",
c.Name,
wsInstrumentMap.LookupInstrument(response.Results[i].InstID),
response.Results[i].OrderID,
response.Results[i].Status))
}
}
return &response, errors
return response, err
}
func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) error {
func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) (*WsTradeHistoryResponse, error) {
var response *WsTradeHistoryResponse
if !c.Websocket.CanUseAuthenticatedEndpoints() {
return fmt.Errorf("%v not authorised to get trade history", c.Name)
return response, fmt.Errorf("%v not authorised to get trade history", c.Name)
}
curr := c.FormatExchangeCurrency(p, asset.Spot).String()
var request WsTradeHistoryRequest
request.Request = "trade_history"
request.InstID = wsInstrumentMap.LookupID(curr)
request.InstID = c.instrumentMap.LookupID(curr)
request.Nonce = c.WebsocketConn.GenerateMessageID(false)
request.Start = start
request.Limit = limit
resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request)
if err != nil {
return err
return response, err
}
var response map[string]interface{}
err = json.Unmarshal(resp, &response)
if err != nil {
return err
return response, err
}
if response["status"].([]interface{})[0] != "OK" {
return fmt.Errorf("%v get trade history failed for %v",
if response.Status[0] != "OK" {
return response, fmt.Errorf("%v get trade history failed for %v",
c.Name,
request)
}
return nil
return response, nil
}

View File

@@ -88,15 +88,20 @@ func (c *COINUT) SetDefaults() {
FiatWithdrawalFee: true,
},
WebsocketCapabilities: protocol.Features{
AccountBalance: true,
GetOrders: true,
CancelOrders: true,
CancelOrder: true,
SubmitOrder: true,
SubmitOrders: true,
UserTradeHistory: true,
TickerFetching: true,
OrderbookFetching: true,
TradeFetching: true,
OrderbookFetching: true,
AccountInfo: true,
Subscribe: true,
Unsubscribe: true,
AuthenticatedEndpoints: true,
SubmitOrder: true,
SubmitOrders: true,
CancelOrder: true,
MessageCorrelation: true,
},
WithdrawPermissions: exchange.WithdrawCryptoViaWebsiteOnly |
@@ -217,15 +222,25 @@ func (c *COINUT) Run() {
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (c *COINUT) FetchTradablePairs(asset asset.Item) ([]string, error) {
i, err := c.GetInstruments()
if err != nil {
return nil, err
var instruments map[string][]InstrumentBase
var resp Instruments
var err error
if c.Websocket.IsConnected() {
resp, err = c.WsGetInstruments()
if err != nil {
return nil, err
}
} else {
resp, err = c.GetInstruments()
if err != nil {
return nil, err
}
}
instruments = resp.Instruments
var pairs []string
for _, y := range i.Instruments {
c.instrumentMap.Seed(y[0].Base+y[0].Quote, y[0].InstID)
p := y[0].Base + c.GetPairFormat(asset, false).Delimiter + y[0].Quote
for i := range instruments {
c.instrumentMap.Seed(instruments[i][0].Base+instruments[i][0].Quote, instruments[i][0].InstID)
p := instruments[i][0].Base + c.GetPairFormat(asset, false).Delimiter + instruments[i][0].Quote
pairs = append(pairs, p)
}
@@ -248,9 +263,20 @@ func (c *COINUT) UpdateTradablePairs(forceUpdate bool) error {
// COINUT exchange
func (c *COINUT) GetAccountInfo() (exchange.AccountInfo, error) {
var info exchange.AccountInfo
bal, err := c.GetUserBalance()
if err != nil {
return info, err
var bal *UserBalance
var err error
if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var resp *UserBalance
resp, err = c.wsGetAccountBalance()
if err != nil {
return info, err
}
bal = resp
} else {
bal, err = c.GetUserBalance()
if err != nil {
return info, err
}
}
var balances = []exchange.AccountCurrencyInfo{
@@ -322,12 +348,9 @@ func (c *COINUT) GetAccountInfo() (exchange.AccountInfo, error) {
// UpdateTicker updates and returns the ticker for a currency pair
func (c *COINUT) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Price, error) {
var tickerPrice ticker.Price
if !c.instrumentMap.IsLoaded() {
err := c.SeedInstruments()
if err != nil {
return tickerPrice, err
}
err := c.loadInstrumentsIfNotLoaded()
if err != nil {
return tickerPrice, err
}
instID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(p,
@@ -335,8 +358,8 @@ func (c *COINUT) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pri
if instID == 0 {
return tickerPrice, errors.New("unable to lookup instrument ID")
}
tick, err := c.GetInstrumentTicker(instID)
var tick Ticker
tick, err = c.GetInstrumentTicker(instID)
if err != nil {
return tickerPrice, err
}
@@ -379,12 +402,9 @@ func (c *COINUT) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderboo
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (c *COINUT) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
if !c.instrumentMap.IsLoaded() {
err := c.SeedInstruments()
if err != nil {
return orderBook, err
}
err := c.loadInstrumentsIfNotLoaded()
if err != nil {
return orderBook, err
}
instID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(p,
@@ -430,69 +450,74 @@ func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]ex
}
// SubmitOrder submits a new order
func (c *COINUT) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
func (c *COINUT) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
return submitOrderResponse, err
var err error
if _, err = strconv.Atoi(o.ClientID); err != nil {
return submitOrderResponse, fmt.Errorf("%s - ClientID must be a number, received: %s", c.Name, o.ClientID)
}
err = o.Validate()
var APIresponse interface{}
isBuyOrder := s.OrderSide == order.Buy
clientIDInt, err := strconv.ParseUint(s.ClientID, 0, 32)
if err != nil {
return submitOrderResponse, err
}
clientIDUint := uint32(clientIDInt)
if !c.instrumentMap.IsLoaded() {
err = c.SeedInstruments()
if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var response *WsStandardOrderResponse
response, err = c.wsSubmitOrder(&WsSubmitOrderParameters{
Currency: o.Pair,
Side: o.OrderSide,
Amount: o.Amount,
Price: o.Price,
})
if err != nil {
return submitOrderResponse, err
}
}
currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(s.Pair,
asset.Spot).String())
if currencyID == 0 {
return submitOrderResponse, errLookupInstrumentID
}
switch s.OrderType {
case order.Limit:
APIresponse, err = c.NewOrder(currencyID,
s.Amount,
s.Price,
isBuyOrder,
clientIDUint)
case order.Market:
APIresponse, err = c.NewOrder(currencyID,
s.Amount,
0,
isBuyOrder,
clientIDUint)
}
switch apiResp := APIresponse.(type) {
case OrdersBase:
orderResult := apiResp
submitOrderResponse.OrderID = strconv.FormatInt(orderResult.OrderID, 10)
case OrderFilledResponse:
orderResult := apiResp
submitOrderResponse.OrderID = strconv.FormatInt(orderResult.Order.OrderID, 10)
case OrderRejectResponse:
orderResult := apiResp
submitOrderResponse.OrderID = strconv.FormatInt(orderResult.OrderID, 10)
err = fmt.Errorf("orderID: %d was rejected: %v",
orderResult.OrderID,
orderResult.Reasons)
}
if err == nil {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10)
submitOrderResponse.IsOrderPlaced = true
}
} else {
err = c.loadInstrumentsIfNotLoaded()
if err != nil {
return submitOrderResponse, err
}
return submitOrderResponse, err
currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(o.Pair,
asset.Spot).String())
if currencyID == 0 {
return submitOrderResponse, errLookupInstrumentID
}
var APIResponse interface{}
var clientIDInt uint64
isBuyOrder := o.OrderSide == order.Buy
clientIDInt, err = strconv.ParseUint(o.ClientID, 0, 32)
if err != nil {
return submitOrderResponse, err
}
clientIDUint := uint32(clientIDInt)
APIResponse, err = c.NewOrder(currencyID, o.Amount, o.Price,
isBuyOrder, clientIDUint)
if err != nil {
return submitOrderResponse, err
}
responseMap := APIResponse.(map[string]interface{})
switch responseMap["reply"].(string) {
case "order_rejected":
return submitOrderResponse, fmt.Errorf("clientOrderID: %v was rejected: %v", o.ClientID, responseMap["reasons"])
case "order_filled":
orderID := responseMap["order_id"].(float64)
submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64)
submitOrderResponse.IsOrderPlaced = true
submitOrderResponse.FullyMatched = true
return submitOrderResponse, nil
case "order_accepted":
orderID := responseMap["order_id"].(float64)
submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64)
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, nil
}
}
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -502,76 +527,108 @@ func (c *COINUT) ModifyOrder(action *order.Modify) (string, error) {
}
// CancelOrder cancels an order by its corresponding ID number
func (c *COINUT) CancelOrder(order *order.Cancel) error {
orderIDInt, err := strconv.ParseInt(order.OrderID, 10, 64)
func (c *COINUT) CancelOrder(o *order.Cancel) error {
err := c.loadInstrumentsIfNotLoaded()
if err != nil {
return err
}
orderIDInt, err := strconv.ParseInt(o.OrderID, 10, 64)
if err != nil {
return err
}
if !c.instrumentMap.IsLoaded() {
err = c.SeedInstruments()
currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(
o.CurrencyPair,
asset.Spot).String(),
)
if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var resp *CancelOrdersResponse
resp, err = c.wsCancelOrder(&WsCancelOrderParameters{
Currency: o.CurrencyPair,
OrderID: orderIDInt,
})
if err != nil {
return err
}
if len(resp.Status) >= 1 && resp.Status[0] != "OK" {
return errors.New(c.Name + " - Failed to cancel order " + o.OrderID)
}
} else {
if currencyID == 0 {
return errLookupInstrumentID
}
_, err = c.CancelExistingOrder(currencyID, orderIDInt)
if err != nil {
return err
}
}
currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(
order.CurrencyPair,
asset.Spot).String(),
)
if currencyID == 0 {
return errLookupInstrumentID
}
_, err = c.CancelExistingOrder(currencyID, orderIDInt)
return err
return nil
}
// CancelAllOrders cancels all orders associated with a currency pair
func (c *COINUT) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
// TODO, this is a terrible implementation. Requires DB to improve
// Coinut provides no way of retrieving orders without a currency
// So we need to retrieve all currencies, then retrieve orders for each
// currency then cancel. Advisable to never use this until DB due to
// performance.
cancelAllOrdersResponse := order.CancelAllResponse{
Status: make(map[string]string),
func (c *COINUT) CancelAllOrders(details *order.Cancel) (order.CancelAllResponse, error) {
var cancelAllOrdersResponse order.CancelAllResponse
err := c.loadInstrumentsIfNotLoaded()
if err != nil {
return cancelAllOrdersResponse, err
}
if !c.instrumentMap.IsLoaded() {
err := c.SeedInstruments()
cancelAllOrdersResponse.Status = make(map[string]string)
if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
openOrders, err := c.wsGetOpenOrders(details.CurrencyPair.String())
if err != nil {
return cancelAllOrdersResponse, err
}
}
var allTheOrders []OrderResponse
ids := c.instrumentMap.GetInstrumentIDs()
for x := range ids {
openOrders, err := c.GetOpenOrders(ids[x])
var ordersToCancel []WsCancelOrderParameters
for i := range openOrders.Orders {
if openOrders.Orders[i].InstID == c.instrumentMap.LookupID(c.FormatExchangeCurrency(details.CurrencyPair, asset.Spot).String()) {
ordersToCancel = append(ordersToCancel, WsCancelOrderParameters{
Currency: details.CurrencyPair,
OrderID: openOrders.Orders[i].OrderID,
})
}
}
resp, err := c.wsCancelOrders(ordersToCancel)
if err != nil {
return cancelAllOrdersResponse, err
}
allTheOrders = append(allTheOrders, openOrders.Orders...)
}
var allTheOrdersToCancel []CancelOrders
for _, orderToCancel := range allTheOrders {
cancelOrder := CancelOrders{
InstrumentID: orderToCancel.InstrumentID,
OrderID: orderToCancel.OrderID,
for i := range resp.Results {
if openOrders.Orders[i].Status[0] != "OK" {
cancelAllOrdersResponse.Status[strconv.FormatInt(openOrders.Orders[i].OrderID, 10)] = strings.Join(openOrders.Orders[i].Status, ",")
}
}
allTheOrdersToCancel = append(allTheOrdersToCancel, cancelOrder)
}
if len(allTheOrdersToCancel) > 0 {
resp, err := c.CancelOrders(allTheOrdersToCancel)
if err != nil {
return cancelAllOrdersResponse, err
} else {
var allTheOrders []OrderResponse
ids := c.instrumentMap.GetInstrumentIDs()
for x := range ids {
if ids[x] == c.instrumentMap.LookupID(c.FormatExchangeCurrency(details.CurrencyPair, asset.Spot).String()) {
openOrders, err := c.GetOpenOrders(ids[x])
if err != nil {
return cancelAllOrdersResponse, err
}
allTheOrders = append(allTheOrders, openOrders.Orders...)
}
}
for _, order := range resp.Results {
if order.Status != "OK" {
cancelAllOrdersResponse.Status[strconv.FormatInt(order.OrderID, 10)] = order.Status
var allTheOrdersToCancel []CancelOrders
for i := range allTheOrders {
cancelOrder := CancelOrders{
InstrumentID: allTheOrders[i].InstrumentID,
OrderID: allTheOrders[i].OrderID,
}
allTheOrdersToCancel = append(allTheOrdersToCancel, cancelOrder)
}
if len(allTheOrdersToCancel) > 0 {
resp, err := c.CancelOrders(allTheOrdersToCancel)
if err != nil {
return cancelAllOrdersResponse, err
}
for i := range resp.Results {
if resp.Results[i].Status != "OK" {
cancelAllOrdersResponse.Status[strconv.FormatInt(resp.Results[i].OrderID, 10)] = resp.Results[i].Status
}
}
}
}
@@ -623,51 +680,81 @@ func (c *COINUT) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error)
// GetActiveOrders retrieves any orders that are active/open
func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) {
if !c.instrumentMap.IsLoaded() {
err := c.SeedInstruments()
if err != nil {
return nil, err
}
err := c.loadInstrumentsIfNotLoaded()
if err != nil {
return nil, err
}
var instrumentsToUse []int64
if len(req.Currencies) > 0 {
for x := range req.Currencies {
currency := c.FormatExchangeCurrency(req.Currencies[x],
asset.Spot).String()
instrumentsToUse = append(instrumentsToUse,
c.instrumentMap.LookupID(currency))
var orders []order.Detail
var currenciesToCheck []string
if len(req.Currencies) == 0 {
for i := range req.Currencies {
currenciesToCheck = append(currenciesToCheck, c.FormatExchangeCurrency(req.Currencies[i], asset.Spot).String())
}
} else {
instrumentsToUse = c.instrumentMap.GetInstrumentIDs()
}
if len(instrumentsToUse) == 0 {
return nil, errors.New("no instrument IDs to use")
}
var orders []order.Detail
for x := range instrumentsToUse {
openOrders, err := c.GetOpenOrders(instrumentsToUse[x])
if err != nil {
return nil, err
for k := range c.instrumentMap.Instruments {
currenciesToCheck = append(currenciesToCheck, k)
}
for y := range openOrders.Orders {
curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x])
p := currency.NewPairFromFormattedPairs(curr,
c.GetEnabledPairs(asset.Spot),
c.GetPairFormat(asset.Spot, true))
orderSide := order.Side(strings.ToUpper(openOrders.Orders[y].Side))
orderDate := time.Unix(openOrders.Orders[y].Timestamp, 0)
orders = append(orders, order.Detail{
ID: strconv.FormatInt(openOrders.Orders[y].OrderID, 10),
Amount: openOrders.Orders[y].Quantity,
Price: openOrders.Orders[y].Price,
Exchange: c.Name,
OrderSide: orderSide,
OrderDate: orderDate,
CurrencyPair: p,
})
}
if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
for x := range currenciesToCheck {
openOrders, err := c.wsGetOpenOrders(currenciesToCheck[x])
if err != nil {
return nil, err
}
for i := range openOrders.Orders {
orders = append(orders, order.Detail{
Exchange: c.Name,
ID: strconv.FormatInt(openOrders.Orders[i].OrderID, 10),
CurrencyPair: c.FormatExchangeCurrency(currency.NewPairFromString(currenciesToCheck[x]), asset.Spot),
OrderSide: order.Side(openOrders.Orders[i].Side),
OrderDate: time.Unix(0, openOrders.Orders[i].Timestamp),
Status: order.Active,
Price: openOrders.Orders[i].Price,
Amount: openOrders.Orders[i].Qty,
ExecutedAmount: openOrders.Orders[i].Qty - openOrders.Orders[i].OpenQty,
RemainingAmount: openOrders.Orders[i].OpenQty,
})
}
}
} else {
var instrumentsToUse []int64
if len(req.Currencies) > 0 {
for x := range req.Currencies {
curr := c.FormatExchangeCurrency(req.Currencies[x],
asset.Spot).String()
instrumentsToUse = append(instrumentsToUse,
c.instrumentMap.LookupID(curr))
}
} else {
instrumentsToUse = c.instrumentMap.GetInstrumentIDs()
}
if len(instrumentsToUse) == 0 {
return nil, errors.New("no instrument IDs to use")
}
for x := range instrumentsToUse {
openOrders, err := c.GetOpenOrders(instrumentsToUse[x])
if err != nil {
return nil, err
}
for y := range openOrders.Orders {
curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x])
p := currency.NewPairFromFormattedPairs(curr,
c.GetEnabledPairs(asset.Spot),
c.GetPairFormat(asset.Spot, true))
orderSide := order.Side(strings.ToUpper(openOrders.Orders[y].Side))
orderDate := time.Unix(openOrders.Orders[y].Timestamp, 0)
orders = append(orders, order.Detail{
ID: strconv.FormatInt(openOrders.Orders[y].OrderID, 10),
Amount: openOrders.Orders[y].Quantity,
Price: openOrders.Orders[y].Price,
Exchange: c.Name,
OrderSide: orderSide,
OrderDate: orderDate,
CurrencyPair: p,
})
}
}
}
@@ -679,51 +766,78 @@ func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e
// GetOrderHistory retrieves account order information
// Can Limit response to specific order status
func (c *COINUT) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) {
if !c.instrumentMap.IsLoaded() {
err := c.SeedInstruments()
if err != nil {
return nil, err
}
err := c.loadInstrumentsIfNotLoaded()
if err != nil {
return nil, err
}
var instrumentsToUse []int64
if len(req.Currencies) > 0 {
for x := range req.Currencies {
currency := c.FormatExchangeCurrency(req.Currencies[x],
asset.Spot).String()
instrumentsToUse = append(instrumentsToUse,
c.instrumentMap.LookupID(currency))
var allOrders []order.Detail
if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
for i := range req.Currencies {
for j := int64(0); ; j += 100 {
trades, err := c.wsGetTradeHistory(req.Currencies[i], j, 100)
if err != nil {
return allOrders, err
}
for x := range trades.Trades {
curr := c.instrumentMap.LookupInstrument(trades.Trades[x].InstID)
allOrders = append(allOrders, order.Detail{
Exchange: c.Name,
ID: strconv.FormatInt(trades.Trades[x].OrderID, 10),
CurrencyPair: currency.NewPairFromString(curr),
OrderSide: order.Side(trades.Trades[x].Side),
OrderDate: time.Unix(0, trades.Trades[x].Timestamp),
Status: order.Filled,
Price: trades.Trades[x].Price,
Amount: trades.Trades[x].Qty,
ExecutedAmount: trades.Trades[x].Qty,
RemainingAmount: trades.Trades[x].OpenQty,
})
}
if len(trades.Trades) < 100 {
break
}
}
}
} else {
instrumentsToUse = c.instrumentMap.GetInstrumentIDs()
}
if len(instrumentsToUse) == 0 {
return nil, errors.New("no instrument IDs to use")
}
var allOrders []order.Detail
for x := range instrumentsToUse {
orders, err := c.GetTradeHistory(instrumentsToUse[x], -1, -1)
if err != nil {
return nil, err
var instrumentsToUse []int64
if len(req.Currencies) > 0 {
for x := range req.Currencies {
curr := c.FormatExchangeCurrency(req.Currencies[x],
asset.Spot).String()
instrumentID := c.instrumentMap.LookupID(curr)
if instrumentID > 0 {
instrumentsToUse = append(instrumentsToUse, instrumentID)
}
}
} else {
instrumentsToUse = c.instrumentMap.GetInstrumentIDs()
}
for y := range orders.Trades {
curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x])
p := currency.NewPairFromFormattedPairs(curr,
c.GetEnabledPairs(asset.Spot),
c.GetPairFormat(asset.Spot, true))
orderSide := order.Side(strings.ToUpper(orders.Trades[y].Order.Side))
orderDate := time.Unix(orders.Trades[y].Order.Timestamp, 0)
allOrders = append(allOrders, order.Detail{
ID: strconv.FormatInt(orders.Trades[y].Order.OrderID, 10),
Amount: orders.Trades[y].Order.Quantity,
Price: orders.Trades[y].Order.Price,
Exchange: c.Name,
OrderSide: orderSide,
OrderDate: orderDate,
CurrencyPair: p,
})
if len(instrumentsToUse) == 0 {
return nil, errors.New("no instrument IDs to use")
}
for x := range instrumentsToUse {
orders, err := c.GetTradeHistory(instrumentsToUse[x], -1, -1)
if err != nil {
return nil, err
}
for y := range orders.Trades {
curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x])
p := currency.NewPairFromFormattedPairs(curr,
c.GetEnabledPairs(asset.Spot),
c.GetPairFormat(asset.Spot, true))
orderSide := order.Side(strings.ToUpper(orders.Trades[y].Order.Side))
orderDate := time.Unix(orders.Trades[y].Order.Timestamp, 0)
allOrders = append(allOrders, order.Detail{
ID: strconv.FormatInt(orders.Trades[y].Order.OrderID, 10),
Amount: orders.Trades[y].Order.Quantity,
Price: orders.Trades[y].Order.Price,
Exchange: c.Name,
OrderSide: orderSide,
OrderDate: orderDate,
CurrencyPair: p,
})
}
}
}
@@ -755,3 +869,20 @@ func (c *COINUT) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, e
func (c *COINUT) AuthenticateWebsocket() error {
return c.wsAuthenticate()
}
func (c *COINUT) loadInstrumentsIfNotLoaded() error {
if !c.instrumentMap.IsLoaded() {
if c.Websocket.IsConnected() {
_, err := c.WsGetInstruments()
if err != nil {
return err
}
} else {
err := c.SeedInstruments()
if err != nil {
return err
}
}
}
return nil
}

View File

@@ -14,27 +14,8 @@ import (
const (
RestAuthentication uint8 = 0
WebsocketAuthentication uint8 = 1
)
// FeeType custom type for calculating fees based on method
type FeeType uint8
// Const declarations for fee types
const (
BankFee FeeType = iota
InternationalBankDepositFee
InternationalBankWithdrawalFee
CryptocurrencyTradeFee
CyptocurrencyDepositFee
CryptocurrencyWithdrawalFee
OfflineTradeFee
)
// InternationalBankTransactionType custom type for calculating fees based on fiat transaction types
type InternationalBankTransactionType uint8
// Const declarations for international transaction types
const (
// Repeated exchange strings
// FeeType custom type for calculating fees based on method
WireTransfer InternationalBankTransactionType = iota
PerfectMoney
Neteller
@@ -53,26 +34,15 @@ const (
WesternUnion
MoneyGram
Contact
)
// FeeBuilder is the type which holds all parameters required to calculate a fee
// for an exchange
type FeeBuilder struct {
FeeType FeeType
// Used for calculating crypto trading fees, deposits & withdrawals
Pair currency.Pair
IsMaker bool
// Fiat currency used for bank deposits & withdrawals
FiatCurrency currency.Code
BankTransactionType InternationalBankTransactionType
// Used to multiply for fee calculations
PurchasePrice float64
Amount float64
}
// Definitions for each type of withdrawal method for a given exchange
const (
// No withdraw
// Const declarations for fee types
BankFee FeeType = iota
InternationalBankDepositFee
InternationalBankWithdrawalFee
CryptocurrencyTradeFee
CyptocurrencyDepositFee
CryptocurrencyWithdrawalFee
OfflineTradeFee
// Definitions for each type of withdrawal method for a given exchange
NoAPIWithdrawalMethods uint32 = 0
NoAPIWithdrawalMethodsText string = "NONE, WEBSITE ONLY"
AutoWithdrawCrypto uint32 = (1 << 0)
@@ -113,10 +83,29 @@ const (
WithdrawFiatViaWebsiteOnlyText string = "WITHDRAW FIAT VIA WEBSITE ONLY"
NoFiatWithdrawals uint32 = (1 << 18)
NoFiatWithdrawalsText string = "NO FIAT WITHDRAWAL"
UnknownWithdrawalTypeText string = "UNKNOWN"
UnknownWithdrawalTypeText string = "UNKNOWN"
)
type FeeType uint8
// InternationalBankTransactionType custom type for calculating fees based on fiat transaction types
type InternationalBankTransactionType uint8
// FeeBuilder is the type which holds all parameters required to calculate a fee
// for an exchange
type FeeBuilder struct {
FeeType FeeType
// Used for calculating crypto trading fees, deposits & withdrawals
Pair currency.Pair
IsMaker bool
// Fiat currency used for bank deposits & withdrawals
FiatCurrency currency.Code
BankTransactionType InternationalBankTransactionType
// Used to multiply for fee calculations
PurchasePrice float64
Amount float64
}
// AccountInfo is a Generic type to hold each exchange's holdings in
// all enabled currencies
type AccountInfo struct {

View File

@@ -2,6 +2,7 @@ package exmo
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -21,11 +22,8 @@ var (
e EXMO
)
func TestDefault(t *testing.T) {
func TestMain(m *testing.M) {
e.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
@@ -33,17 +31,19 @@ func TestSetup(t *testing.T) {
}
exmoConf, err := cfg.GetExchangeConfig("EXMO")
if err != nil {
t.Error("Exmo Setup() init error")
log.Fatal("Exmo Setup() init error")
}
err = e.Setup(exmoConf)
if err != nil {
t.Fatal("Exmo setup error", err)
log.Fatal("Exmo setup error", err)
}
e.API.AuthenticatedSupport = true
e.API.Credentials.Key = APIKey
e.API.Credentials.Secret = APISecret
os.Exit(m.Run())
}
func TestGetTrades(t *testing.T) {
@@ -88,10 +88,9 @@ func TestGetCurrency(t *testing.T) {
func TestGetUserInfo(t *testing.T) {
t.Parallel()
if APIKey == "" || APISecret == "" {
if !areTestAPIKeysSet() {
t.Skip()
}
TestSetup(t)
_, err := e.GetUserInfo()
if err != nil {
t.Errorf("Err: %s", err)
@@ -100,10 +99,9 @@ func TestGetUserInfo(t *testing.T) {
func TestGetRequiredAmount(t *testing.T) {
t.Parallel()
if APIKey == "" || APISecret == "" {
if !areTestAPIKeysSet() {
t.Skip()
}
TestSetup(t)
_, err := e.GetRequiredAmount("BTC_USD", 100)
if err != nil {
t.Errorf("Err: %s", err)
@@ -125,7 +123,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
e.GetFeeByType(feeBuilder)
if APIKey == "" || APISecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -137,8 +135,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
e.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
@@ -255,20 +251,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
e.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := e.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
e.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -282,9 +272,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
e.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -307,8 +294,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
e.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -334,14 +319,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
e.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -359,9 +341,6 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
e.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -389,6 +368,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := e.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -396,8 +378,10 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
e.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -407,10 +391,6 @@ func TestWithdraw(t *testing.T) {
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := e.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
@@ -421,9 +401,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
e.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -436,9 +413,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
e.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}

View File

@@ -238,10 +238,10 @@ func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook
if err != nil {
return orderBook, err
}
for _, x := range e.GetEnabledPairs(assetType) {
currency := e.FormatExchangeCurrency(x, assetType)
data, ok := result[currency.String()]
enabledPairs := e.GetEnabledPairs(assetType)
for i := range enabledPairs {
curr := e.FormatExchangeCurrency(enabledPairs[i], assetType)
data, ok := result[curr.String()]
if !ok {
continue
}
@@ -266,7 +266,7 @@ func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook
}
orderBook.Bids = obItems
orderBook.Pair = x
orderBook.Pair = enabledPairs[i]
orderBook.ExchangeName = e.Name
orderBook.AssetType = assetType
@@ -344,15 +344,18 @@ func (e *EXMO) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
oT,
s.Price,
s.Amount)
if err != nil {
return submitOrderResponse, err
}
if response > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -382,10 +385,10 @@ func (e *EXMO) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error)
return cancelAllOrdersResponse, err
}
for _, order := range openOrders {
err = e.CancelExistingOrder(order.OrderID)
for i := range openOrders {
err = e.CancelExistingOrder(openOrders[i].OrderID)
if err != nil {
cancelAllOrdersResponse.Status[strconv.FormatInt(order.OrderID, 10)] = err.Error()
cancelAllOrdersResponse.Status[strconv.FormatInt(openOrders[i].OrderID, 10)] = err.Error()
}
}
@@ -486,13 +489,13 @@ func (e *EXMO) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, err
}
var allTrades []UserTrades
for _, currency := range req.Currencies {
resp, err := e.GetUserTrades(e.FormatExchangeCurrency(currency, asset.Spot).String(), "", "10000")
for i := range req.Currencies {
resp, err := e.GetUserTrades(e.FormatExchangeCurrency(req.Currencies[i], asset.Spot).String(), "", "10000")
if err != nil {
return nil, err
}
for _, order := range resp {
allTrades = append(allTrades, order...)
for j := range resp {
allTrades = append(allTrades, resp[j]...)
}
}

View File

@@ -1,7 +1,9 @@
package gateio
import (
"log"
"net/http"
"os"
"testing"
"github.com/gorilla/websocket"
@@ -25,29 +27,28 @@ const (
var g Gateio
var wsSetupRan bool
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
g.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("GateIO load config error", err)
log.Fatal("GateIO load config error", err)
}
gateioConfig, err := cfg.GetExchangeConfig("GateIO")
gConf, err := cfg.GetExchangeConfig("GateIO")
if err != nil {
t.Error("GateIO Setup() init error")
log.Fatal("GateIO Setup() init error")
}
gateioConfig.API.AuthenticatedSupport = true
gateioConfig.API.AuthenticatedWebsocketSupport = true
gateioConfig.API.Credentials.Key = apiKey
gateioConfig.API.Credentials.Secret = apiSecret
gConf.API.AuthenticatedSupport = true
gConf.API.AuthenticatedWebsocketSupport = true
gConf.API.Credentials.Key = apiKey
gConf.API.Credentials.Secret = apiSecret
err = g.Setup(gateioConfig)
err = g.Setup(gConf)
if err != nil {
t.Fatal("GateIO setup error", err)
log.Fatal("GateIO setup error", err)
}
os.Exit(m.Run())
}
func TestGetSymbols(t *testing.T) {
@@ -100,7 +101,7 @@ func TestCancelExistingOrder(t *testing.T) {
func TestGetBalances(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
t.Skip()
}
@@ -173,7 +174,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
g.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -185,9 +186,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
g.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
if areTestAPIKeysSet() {
// CryptocurrencyTradeFee Basic
@@ -265,20 +263,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
g.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := g.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
g.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -292,9 +284,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
g.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -318,9 +307,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip()
}
@@ -346,15 +332,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip()
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -372,15 +354,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip()
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -417,6 +395,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := g.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -424,8 +405,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
g.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -449,9 +428,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -464,9 +440,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -492,9 +465,6 @@ func TestGetDepositAddress(t *testing.T) {
}
}
func TestGetOrderInfo(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if !areTestAPIKeysSet() {
t.Skip("no API keys set skipping test")
}
@@ -509,8 +479,6 @@ func TestGetOrderInfo(t *testing.T) {
// TestWsGetBalance dials websocket, sends balance request.
func TestWsGetBalance(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
@@ -541,12 +509,14 @@ func TestWsGetBalance(t *testing.T) {
if err != nil {
t.Error(err)
}
_, err = g.wsGetBalance([]string{})
if err != nil {
t.Error(err)
}
}
// TestWsGetOrderInfo dials websocket, sends order info request.
func TestWsGetOrderInfo(t *testing.T) {
g.SetDefaults()
TestSetup(t)
if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
@@ -573,7 +543,7 @@ func TestWsGetOrderInfo(t *testing.T) {
if resp.Result.Status != "success" {
t.Fatal("Unsuccessful login")
}
_, err = g.wsGetOrderInfo("EOS_USDT", 0, 10)
_, err = g.wsGetOrderInfo("EOS_USDT", 0, 1000)
if err != nil {
t.Error(err)
}
@@ -583,8 +553,6 @@ func setupWSTestAuth(t *testing.T) {
if wsSetupRan {
return
}
g.SetDefaults()
TestSetup(t)
if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport {
t.Skip(wshandler.WebsocketNotEnabled)
}

View File

@@ -445,19 +445,19 @@ type WebSocketOrderQueryResult struct {
// WebSocketOrderQueryRecords contains order information from a order.query websocket request
type WebSocketOrderQueryRecords struct {
ID int `json:"id"`
ID int64 `json:"id"`
Market string `json:"market"`
User int `json:"user"`
User int64 `json:"user"`
Ctime float64 `json:"ctime"`
Mtime float64 `json:"mtime"`
Price string `json:"price"`
Amount string `json:"amount"`
Left string `json:"left"`
DealFee string `json:"dealFee"`
OrderType int `json:"orderType"`
Type int `json:"type"`
FilledAmount string `json:"filledAmount"`
FilledTotal string `json:"filledTotal"`
Price float64 `json:"price,string"`
Amount float64 `json:"amount,string"`
Left float64 `json:"left,string"`
DealFee float64 `json:"dealFee,string"`
OrderType int64 `json:"orderType"`
Type int64 `json:"type"`
FilledAmount float64 `json:"filledAmount,string"`
FilledTotal float64 `json:"filledTotal,string"`
}
// WebsocketAuthenticationResponse contains the result of a login request
@@ -473,14 +473,14 @@ type WebsocketAuthenticationResponse struct {
type wsGetBalanceRequest struct {
ID int64 `json:"id"`
Method string `json:"method"`
Params []string `json:"params,omitempty"`
Params []string `json:"params"`
}
// WsGetBalanceResponse stores WS GetBalance response
type WsGetBalanceResponse struct {
Error interface{} `json:"error"`
Result map[currency.Code]WsGetBalanceResponseData `json:"result,omitempty"`
ID int64 `json:"id"`
Error interface{} `json:"error"`
Result map[string]WsGetBalanceResponseData `json:"result"`
ID int64 `json:"id"`
}
// WsGetBalanceResponseData contains currency data

View File

@@ -40,6 +40,7 @@ func (g *Gateio) WsConnect() error {
_, err = g.wsServerSignIn()
if err != nil {
log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", g.Name, err)
g.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
g.GenerateAuthenticatedSubscriptions()
g.GenerateDefaultSubscriptions()
@@ -416,7 +417,7 @@ func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) (*WebSocketOrd
if !g.Websocket.CanUseAuthenticatedEndpoints() {
return nil, fmt.Errorf("%v not authorised to get order info", g.Name)
}
order := WebsocketRequest{
ord := WebsocketRequest{
ID: g.WebsocketConn.GenerateMessageID(true),
Method: "order.query",
Params: []interface{}{
@@ -425,7 +426,7 @@ func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) (*WebSocketOrd
limit,
},
}
resp, err := g.WebsocketConn.SendMessageReturnResponse(order.ID, order)
resp, err := g.WebsocketConn.SendMessageReturnResponse(ord.ID, ord)
if err != nil {
return nil, err
}

View File

@@ -9,6 +9,7 @@ import (
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -99,6 +100,8 @@ func (g *Gateio) SetDefaults() {
Unsubscribe: true,
AuthenticatedEndpoints: true,
MessageCorrelation: true,
GetOrder: true,
AccountBalance: true,
},
WithdrawPermissions: exchange.AutoWithdrawCrypto |
exchange.NoFiatWithdrawals,
@@ -269,9 +272,9 @@ func (g *Gateio) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderboo
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (g *Gateio) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
currency := g.FormatExchangeCurrency(p, assetType).String()
curr := g.FormatExchangeCurrency(p, assetType).String()
orderbookNew, err := g.GetOrderbook(currency)
orderbookNew, err := g.GetOrderbook(curr)
if err != nil {
return orderBook, err
}
@@ -306,61 +309,78 @@ func (g *Gateio) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbo
// ZB exchange
func (g *Gateio) GetAccountInfo() (exchange.AccountInfo, error) {
var info exchange.AccountInfo
balance, err := g.GetBalances()
if err != nil {
return info, err
}
var balances []exchange.AccountCurrencyInfo
switch l := balance.Locked.(type) {
case map[string]interface{}:
for x := range l {
lockedF, err := strconv.ParseFloat(l[x].(string), 64)
if err != nil {
return info, err
}
balances = append(balances, exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(x),
Hold: lockedF,
if g.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
resp, err := g.wsGetBalance([]string{})
if err != nil {
return info, err
}
var currData []exchange.AccountCurrencyInfo
for k := range resp.Result {
currData = append(currData, exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(k),
TotalValue: resp.Result[k].Available + resp.Result[k].Freeze,
Hold: resp.Result[k].Freeze,
})
}
default:
break
}
info.Accounts = append(info.Accounts, exchange.Account{
Currencies: currData,
})
} else {
balance, err := g.GetBalances()
if err != nil {
return info, err
}
switch v := balance.Available.(type) {
case map[string]interface{}:
for x := range v {
availAmount, err := strconv.ParseFloat(v[x].(string), 64)
if err != nil {
return info, err
}
var updated bool
for i := range balances {
if balances[i].CurrencyName == currency.NewCode(x) {
balances[i].TotalValue = balances[i].Hold + availAmount
updated = true
break
switch l := balance.Locked.(type) {
case map[string]interface{}:
for x := range l {
lockedF, err := strconv.ParseFloat(l[x].(string), 64)
if err != nil {
return info, err
}
}
if !updated {
balances = append(balances, exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(x),
TotalValue: availAmount,
Hold: lockedF,
})
}
default:
break
}
default:
break
}
info.Accounts = append(info.Accounts, exchange.Account{
Currencies: balances,
})
switch v := balance.Available.(type) {
case map[string]interface{}:
for x := range v {
availAmount, err := strconv.ParseFloat(v[x].(string), 64)
if err != nil {
return info, err
}
var updated bool
for i := range balances {
if balances[i].CurrencyName == currency.NewCode(x) {
balances[i].TotalValue = balances[i].Hold + availAmount
updated = true
break
}
}
if !updated {
balances = append(balances, exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(x),
TotalValue: availAmount,
})
}
}
default:
break
}
info.Accounts = append(info.Accounts, exchange.Account{
Currencies: balances,
})
}
info.Exchange = g.Name
@@ -401,15 +421,18 @@ func (g *Gateio) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
}
response, err := g.SpotNewOrder(spotNewOrderRequestParams)
if err != nil {
return submitOrderResponse, err
}
if response.OrderNumber > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
if response.LeftAmount == 0 {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -457,7 +480,6 @@ func (g *Gateio) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, erro
// GetOrderInfo returns information on a current open order
func (g *Gateio) GetOrderInfo(orderID string) (order.Detail, error) {
var orderDetail order.Detail
orders, err := g.GetOpenOrders("")
if err != nil {
return orderDetail, errors.New("failed to get open orders")
@@ -534,40 +556,79 @@ func (g *Gateio) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error)
// GetActiveOrders retrieves any orders that are active/open
func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) {
var orders []order.Detail
var currPair string
if len(req.Currencies) == 1 {
currPair = req.Currencies[0].String()
}
if g.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
for i := 0; ; i += 100 {
resp, err := g.wsGetOrderInfo(req.OrderType.String(), i, 100)
if err != nil {
return orders, err
}
resp, err := g.GetOpenOrders(currPair)
if err != nil {
return nil, err
}
var orders []order.Detail
for i := range resp.Orders {
if resp.Orders[i].Status != "open" {
continue
for j := range resp.WebSocketOrderQueryRecords {
orderSide := order.Buy
if resp.WebSocketOrderQueryRecords[j].Type == 1 {
orderSide = order.Sell
}
orderType := order.Market
if resp.WebSocketOrderQueryRecords[j].OrderType == 1 {
orderType = order.Limit
}
firstNum, decNum, err := convert.SplitFloatDecimals(resp.WebSocketOrderQueryRecords[j].Ctime)
if err != nil {
return orders, err
}
orderDate := time.Unix(firstNum, decNum)
orders = append(orders, order.Detail{
Exchange: g.Name,
AccountID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].User, 10),
ID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].ID, 10),
CurrencyPair: currency.NewPairFromString(resp.WebSocketOrderQueryRecords[j].Market),
OrderSide: orderSide,
OrderType: orderType,
OrderDate: orderDate,
Price: resp.WebSocketOrderQueryRecords[j].Price,
Amount: resp.WebSocketOrderQueryRecords[j].Amount,
ExecutedAmount: resp.WebSocketOrderQueryRecords[j].FilledAmount,
RemainingAmount: resp.WebSocketOrderQueryRecords[j].Left,
Fee: resp.WebSocketOrderQueryRecords[j].DealFee,
})
}
if len(resp.WebSocketOrderQueryRecords) < 100 {
break
}
}
} else {
resp, err := g.GetOpenOrders(currPair)
if err != nil {
return nil, err
}
symbol := currency.NewPairDelimiter(resp.Orders[i].CurrencyPair,
g.GetPairFormat(asset.Spot, false).Delimiter)
side := order.Side(strings.ToUpper(resp.Orders[i].Type))
orderDate := time.Unix(resp.Orders[i].Timestamp, 0)
for i := range resp.Orders {
if resp.Orders[i].Status != "open" {
continue
}
orders = append(orders, order.Detail{
ID: resp.Orders[i].OrderNumber,
Amount: resp.Orders[i].Amount,
Price: resp.Orders[i].Rate,
RemainingAmount: resp.Orders[i].FilledAmount,
OrderDate: orderDate,
OrderSide: side,
Exchange: g.Name,
CurrencyPair: symbol,
Status: order.Status(resp.Orders[i].Status),
})
symbol := currency.NewPairDelimiter(resp.Orders[i].CurrencyPair,
g.GetPairFormat(asset.Spot, false).Delimiter)
side := order.Side(strings.ToUpper(resp.Orders[i].Type))
orderDate := time.Unix(resp.Orders[i].Timestamp, 0)
orders = append(orders, order.Detail{
ID: resp.Orders[i].OrderNumber,
Amount: resp.Orders[i].Amount,
Price: resp.Orders[i].Rate,
RemainingAmount: resp.Orders[i].FilledAmount,
OrderDate: orderDate,
OrderSide: side,
Exchange: g.Name,
CurrencyPair: symbol,
Status: order.Status(resp.Orders[i].Status),
})
}
}
order.FilterOrdersByTickRange(&orders, req.StartTicks, req.EndTicks)
order.FilterOrdersBySide(&orders, req.OrderSide)
return orders, nil
@@ -577,8 +638,8 @@ func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e
// Can Limit response to specific order status
func (g *Gateio) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) {
var trades []TradesResponse
for _, currency := range req.Currencies {
resp, err := g.GetTradeHistory(currency.String())
for i := range req.Currencies {
resp, err := g.GetTradeHistory(req.Currencies[i].String())
if err != nil {
return nil, err
}

View File

@@ -336,9 +336,7 @@ func TestFormatWithdrawPermissions(t *testing.T) {
exchange.AutoWithdrawCryptoWithSetupText +
" & " +
exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := g.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s",
expectedResult,
@@ -425,7 +423,6 @@ func TestCancelExchangeOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &order.Cancel{
OrderID: "266029865",
}
@@ -448,7 +445,6 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",

View File

@@ -32,7 +32,7 @@ const (
)
// Instantiates a communications channel between websocket connections
var comms = make(chan ReadData, 1)
var comms = make(chan ReadData)
var responseMaxLimit time.Duration
var responseCheckTimeout time.Duration
@@ -62,13 +62,13 @@ func (g *Gemini) WsConnect() error {
// WsSubscribe subscribes to the full websocket suite on gemini exchange
func (g *Gemini) WsSubscribe(dialer *websocket.Dialer) error {
enabledCurrencies := g.GetEnabledPairs(asset.Spot)
for i, c := range enabledCurrencies {
for i := range enabledCurrencies {
val := url.Values{}
val.Set("heartbeat", "true")
endpoint := fmt.Sprintf("%s%s/%s?%s",
g.API.Endpoints.WebsocketURL,
geminiWsMarketData,
c.String(),
enabledCurrencies[i].String(),
val.Encode())
connection := &wshandler.WebsocketConnection{
ExchangeName: g.Name,
@@ -82,7 +82,7 @@ func (g *Gemini) WsSubscribe(dialer *websocket.Dialer) error {
return fmt.Errorf("%v Websocket connection %v error. Error %v",
g.Name, endpoint, err)
}
go g.WsReadData(connection, c)
go g.WsReadData(connection, enabledCurrencies[i])
if len(enabledCurrencies)-1 == i {
return nil
}

View File

@@ -333,13 +333,16 @@ func (g *Gemini) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
s.Price,
s.OrderSide.String(),
"exchange limit")
if err != nil {
return submitOrderResponse, err
}
if response > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -369,8 +372,8 @@ func (g *Gemini) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, erro
return cancelAllOrdersResponse, err
}
for _, order := range resp.Details.CancelRejects {
cancelAllOrdersResponse.Status[order] = "Could not cancel order"
for i := range resp.Details.CancelRejects {
cancelAllOrdersResponse.Status[resp.Details.CancelRejects[i]] = "Could not cancel order"
}
return cancelAllOrdersResponse, nil

View File

@@ -315,7 +315,7 @@ func (h *HitBTC) GetOpenOrders(currency string) ([]OrderHistoryResponse, error)
// PlaceOrder places an order on the exchange
func (h *HitBTC) PlaceOrder(currency string, rate, amount float64, orderType, side string) (OrderResponse, error) {
result := OrderResponse{}
var result OrderResponse
values := url.Values{}
values.Set("symbol", currency)

View File

@@ -1,7 +1,9 @@
package hitbtc
import (
"log"
"net/http"
"os"
"testing"
"time"
@@ -26,19 +28,16 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
h.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("HitBTC load config error", err)
log.Fatal("HitBTC load config error", err)
}
hitbtcConfig, err := cfg.GetExchangeConfig("HitBTC")
if err != nil {
t.Error("HitBTC Setup() init error")
log.Fatal("HitBTC Setup() init error")
}
hitbtcConfig.API.AuthenticatedSupport = true
hitbtcConfig.API.AuthenticatedWebsocketSupport = true
@@ -47,8 +46,10 @@ func TestSetup(t *testing.T) {
err = h.Setup(hitbtcConfig)
if err != nil {
t.Fatal("HitBTC setup error", err)
log.Fatal("HitBTC setup error", err)
}
os.Exit(m.Run())
}
func TestGetOrderbook(t *testing.T) {
@@ -94,7 +95,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
h.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -106,9 +107,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestUpdateTicker(t *testing.T) {
h.SetDefaults()
TestSetup(t)
h.CurrencyPairs.StorePairs(asset.Spot, currency.NewPairsFromStrings([]string{"BTC-USD", "XRP-USD"}), true)
_, err := h.UpdateTicker(currency.NewPair(currency.BTC, currency.USD), asset.Spot)
if err != nil {
@@ -136,9 +134,6 @@ func TestGetSingularTicker(t *testing.T) {
}
func TestGetFee(t *testing.T) {
h.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
if areTestAPIKeysSet() {
// CryptocurrencyTradeFee Basic
@@ -219,20 +214,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
h.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := h.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
h.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)},
@@ -247,9 +236,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
h.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)},
@@ -270,9 +256,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -297,15 +280,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -323,15 +302,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -354,6 +329,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := h.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -361,8 +339,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
h.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -386,9 +362,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -401,9 +374,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -432,8 +402,6 @@ func setupWsAuth(t *testing.T) {
if wsSetupRan {
return
}
TestSetDefaults(t)
TestSetup(t)
if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
@@ -395,7 +396,7 @@ func (h *HitBTC) wsLogin() error {
return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", h.Name)
}
h.Websocket.SetCanUseAuthenticatedEndpoints(true)
nonce := fmt.Sprintf("%v", time.Now().Unix())
nonce := strconv.FormatInt(time.Now().Unix(), 10)
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(nonce), []byte(h.API.Credentials.Secret))
request := WsLoginRequest{
Method: "login",
@@ -483,7 +484,7 @@ func (h *HitBTC) wsReplaceOrder(clientOrderID string, quantity, price float64) (
Method: "cancelReplaceOrder",
Params: WsReplaceOrderRequestData{
ClientOrderID: clientOrderID,
RequestClientID: fmt.Sprintf("%v", time.Now().Unix()),
RequestClientID: strconv.FormatInt(time.Now().Unix(), 10),
Quantity: quantity,
Price: price,
},

View File

@@ -373,24 +373,42 @@ func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]ex
}
// SubmitOrder submits a new order
func (h *HitBTC) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
func (h *HitBTC) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
err := o.Validate()
if err != nil {
return submitOrderResponse, err
}
if h.Websocket.IsConnected() && h.Websocket.CanUseAuthenticatedEndpoints() {
var response *WsSubmitOrderSuccessResponse
response, err = h.wsPlaceOrder(o.Pair, o.OrderSide.String(), o.Amount, o.Price)
if err != nil {
return submitOrderResponse, err
}
submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10)
if response.Result.CumQuantity == o.Amount {
submitOrderResponse.FullyMatched = true
}
} else {
var response OrderResponse
response, err = h.PlaceOrder(o.Pair.String(),
o.Price,
o.Amount,
strings.ToLower(o.OrderType.String()),
strings.ToLower(o.OrderSide.String()))
if err != nil {
return submitOrderResponse, err
}
if response.OrderNumber > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10)
}
if o.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
}
submitOrderResponse.IsOrderPlaced = true
response, err := h.PlaceOrder(s.Pair.String(),
s.Price,
s.Amount,
s.OrderType.Lower(),
s.OrderSide.Lower())
if response.OrderNumber > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -44,7 +44,7 @@ const (
huobiOrderCancel = "order/orders/%s/submitcancel"
huobiOrderCancelBatch = "order/orders/batchcancel"
huobiBatchCancelOpenOrders = "order/orders/batchCancelOpenOrders"
huobiGetOrder = "order/orders/%s"
huobiGetOrder = "order/orders/getClientOrder"
huobiGetOrderMatch = "order/orders/%s/matchresults"
huobiGetOrders = "order/orders"
huobiGetOpenOrders = "order/openOrders"
@@ -443,8 +443,9 @@ func (h *HUOBI) GetOrder(orderID int64) (OrderInfo, error) {
}
var result response
endpoint := fmt.Sprintf(huobiGetOrder, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, endpoint, url.Values{}, nil, &result)
urlVal := url.Values{}
urlVal.Set("clientOrderId", strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiGetOrder, urlVal, nil, &result)
if result.ErrorMessage != "" {
return result.Order, errors.New(result.ErrorMessage)
@@ -514,7 +515,7 @@ func (h *HUOBI) GetOrders(symbol, types, start, end, states, from, direct, size
}
// GetOpenOrders returns a list of orders
func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int) ([]OrderInfo, error) {
func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int64) ([]OrderInfo, error) {
type response struct {
Response
Orders []OrderInfo `json:"data"`
@@ -526,7 +527,7 @@ func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int) ([]Order
if len(side) > 0 {
vals.Set("side", side)
}
vals.Set("size", fmt.Sprintf("%v", size))
vals.Set("size", strconv.FormatInt(size, 10))
var result response
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiGetOpenOrders, vals, nil, &result)
@@ -855,8 +856,7 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
values.Set("PrivateSignature", crypto.Base64Encode(privSig))
}
urlPath := fmt.Sprintf("%s%s", common.EncodeURLValues(h.API.Endpoints.URL, values),
endpoint)
urlPath := h.API.Endpoints.URL + common.EncodeURLValues(endpoint, values)
var body []byte

View File

@@ -7,6 +7,7 @@ import (
"encoding/pem"
"io/ioutil"
"log"
"os"
"strconv"
"strings"
"testing"
@@ -33,11 +34,8 @@ const (
var h HUOBI
var wsSetupRan bool
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
h.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
@@ -45,7 +43,7 @@ func TestSetup(t *testing.T) {
}
hConfig, err := cfg.GetExchangeConfig("Huobi")
if err != nil {
t.Error("Huobi Setup() init error")
log.Fatal("Huobi Setup() init error")
}
hConfig.API.AuthenticatedSupport = true
hConfig.API.AuthenticatedWebsocketSupport = true
@@ -54,16 +52,16 @@ func TestSetup(t *testing.T) {
err = h.Setup(hConfig)
if err != nil {
t.Fatal("Huobi setup error", err)
log.Fatal("Huobi setup error", err)
}
os.Exit(m.Run())
}
func setupWsTests(t *testing.T) {
if wsSetupRan {
return
}
TestSetDefaults(t)
TestSetup(t)
if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
@@ -195,8 +193,7 @@ func TestGetTimestamp(t *testing.T) {
func TestGetAccounts(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
if !h.ValidateAPICredentials() || !canManipulateRealOrders {
t.Skip()
}
@@ -208,8 +205,7 @@ func TestGetAccounts(t *testing.T) {
func TestGetAccountBalance(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
if !h.ValidateAPICredentials() || !canManipulateRealOrders {
t.Skip()
}
@@ -227,7 +223,6 @@ func TestGetAccountBalance(t *testing.T) {
func TestGetAggregatedBalance(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
@@ -240,8 +235,7 @@ func TestGetAggregatedBalance(t *testing.T) {
func TestSpotNewOrder(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
if !h.ValidateAPICredentials() || !canManipulateRealOrders {
t.Skip()
}
@@ -261,7 +255,9 @@ func TestSpotNewOrder(t *testing.T) {
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() || !canManipulateRealOrders {
t.Skip()
}
_, err := h.CancelExistingOrder(1337)
if err == nil {
t.Error("Huobi TestCancelExistingOrder Expected error")
@@ -270,16 +266,17 @@ func TestCancelExistingOrder(t *testing.T) {
func TestGetOrder(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() || !canManipulateRealOrders {
t.Skip()
}
_, err := h.GetOrder(1337)
if err == nil {
t.Error("Huobi TestCancelOrder Expected error")
if err != nil {
t.Error(err)
}
}
func TestGetMarginLoanOrders(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
@@ -292,7 +289,6 @@ func TestGetMarginLoanOrders(t *testing.T) {
func TestGetMarginAccountBalance(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() {
t.Skip()
}
@@ -305,7 +301,9 @@ func TestGetMarginAccountBalance(t *testing.T) {
func TestCancelWithdraw(t *testing.T) {
t.Parallel()
if !h.ValidateAPICredentials() || !canManipulateRealOrders {
t.Skip()
}
_, err := h.CancelWithdraw(1337)
if err == nil {
t.Error("Huobi TestCancelWithdraw Expected error")
@@ -314,7 +312,6 @@ func TestCancelWithdraw(t *testing.T) {
func TestPEMLoadAndSign(t *testing.T) {
t.Parallel()
pemKey := strings.NewReader(h.API.Credentials.PEMKey)
pemBytes, err := ioutil.ReadAll(pemKey)
if err != nil {
@@ -355,7 +352,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
h.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -443,20 +440,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
h.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := h.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
h.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.BTC, currency.USDT)},
@@ -471,9 +462,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
h.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.BTC, currency.USDT)},
@@ -494,9 +482,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if !h.ValidateAPICredentials() {
t.Skip()
}
@@ -528,15 +513,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -555,8 +536,6 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -584,7 +563,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestGetAccountInfo(t *testing.T) {
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
_, err := h.GetAccountInfo()
if err == nil {
t.Error("GetAccountInfo() Expected error")
@@ -598,6 +577,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := h.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -605,8 +587,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
h.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -630,9 +610,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -645,9 +622,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
h.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -669,7 +643,7 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsGetAccountsList connects to WS, logs in, gets account list
func TestWsGetAccountsList(t *testing.T) {
setupWsTests(t)
resp, err := h.wsGetAccountsList(currency.NewPairFromString("ethbtc"))
resp, err := h.wsGetAccountsList()
if err != nil {
t.Fatal(err)
}

View File

@@ -170,24 +170,23 @@ type CancelOrderBatch struct {
// OrderInfo stores the order info
type OrderInfo struct {
ID int `json:"id"`
ID int64 `json:"id"`
Symbol string `json:"symbol"`
AccountID float64 `json:"account-id"`
AccountID int64 `json:"account-id"`
Amount float64 `json:"amount,string"`
Price float64 `json:"price,string"`
CreatedAt int64 `json:"created-at"`
Type string `json:"type"`
FieldAmount float64 `json:"field-amount,string"`
FieldCashAmount float64 `json:"field-cash-amount,string"`
Fieldees float64 `json:"field-fees,string"`
FilledAmount float64 `json:"filled-amount,string"`
FilledCashAmount float64 `json:"filled-cash-amount,string"`
FilledFees float64 `json:"filled-fees,string"`
FinishedAt int64 `json:"finished-at"`
UserID int `json:"user-id"`
UserID int64 `json:"user-id"`
Source string `json:"source"`
State string `json:"state"`
CanceledAt int `json:"canceled-at"`
CanceledAt int64 `json:"canceled-at"`
Exchange string `json:"exchange"`
Batch string `json:"batch"`
}
@@ -547,31 +546,13 @@ type WsAuthenticatedAccountsListResponseDataList struct {
// WsAuthenticatedOrdersListResponse response from OrdersList authenticated endpoint
type WsAuthenticatedOrdersListResponse struct {
WsAuthenticatedDataResponse
Data []WsAuthenticatedOrdersListResponseData `json:"data"`
}
// WsAuthenticatedOrdersListResponseData contains order details
type WsAuthenticatedOrdersListResponseData struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
AccountID int64 `json:"account-id"`
Amount float64 `json:"amount,string"`
Price float64 `json:"price,string"`
CreatedAt int64 `json:"created-at"`
Type string `json:"type"`
FilledAmount float64 `json:"filled-amount,string"`
FilledCashAmount float64 `json:"filled-cash-amount,string"`
FilledFees float64 `json:"filled-fees,string"`
FinishedAt int64 `json:"finished-at"`
Source string `json:"source"`
State string `json:"state"`
CanceledAt int64 `json:"canceled-at"`
Data []OrderInfo `json:"data"`
}
// WsAuthenticatedOrderDetailResponse response from OrderDetail authenticated endpoint
type WsAuthenticatedOrderDetailResponse struct {
WsAuthenticatedDataResponse
Data WsAuthenticatedOrdersListResponseData `json:"data"`
Data OrderInfo `json:"data"`
}
// WsPong sent for pong messages

View File

@@ -49,7 +49,7 @@ const (
)
// Instantiates a communications channel between websocket connections
var comms = make(chan WsMessage, 1)
var comms = make(chan WsMessage)
// WsConnect initiates a new websocket connection
func (h *HUOBI) WsConnect() error {
@@ -68,6 +68,7 @@ func (h *HUOBI) WsConnect() error {
err = h.wsLogin()
if err != nil {
log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", h.Name, err)
h.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
go h.WsHandleData()
@@ -434,7 +435,7 @@ func (h *HUOBI) wsAuthenticatedSubscribe(operation, endpoint, topic string) erro
return h.AuthenticatedWebsocketConn.SendMessage(request)
}
func (h *HUOBI) wsGetAccountsList(pair currency.Pair) (*WsAuthenticatedAccountsListResponse, error) {
func (h *HUOBI) wsGetAccountsList() (*WsAuthenticatedAccountsListResponse, error) {
if !h.Websocket.CanUseAuthenticatedEndpoints() {
return nil, fmt.Errorf("%v not authenticated cannot get accounts list", h.Name)
}
@@ -446,7 +447,6 @@ func (h *HUOBI) wsGetAccountsList(pair currency.Pair) (*WsAuthenticatedAccountsL
SignatureVersion: signatureVersion,
Timestamp: timestamp,
Topic: wsAccountsList,
Symbol: h.FormatExchangeCurrency(pair, asset.Spot).String(),
}
hmac := h.wsGenerateSignature(timestamp, wsAccountListEndpoint)
request.Signature = crypto.Base64Encode(hmac)

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
@@ -96,6 +97,8 @@ func (h *HUOBI) SetDefaults() {
AuthenticatedEndpoints: true,
AccountInfo: true,
MessageCorrelation: true,
GetOrder: true,
GetOrders: true,
},
WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup |
exchange.NoFiatWithdrawals,
@@ -393,62 +396,83 @@ func (h *HUOBI) GetAccountID() ([]Account, error) {
func (h *HUOBI) GetAccountInfo() (exchange.AccountInfo, error) {
var info exchange.AccountInfo
info.Exchange = h.Name
accounts, err := h.GetAccountID()
if err != nil {
return info, err
}
for i := range accounts {
var acc exchange.Account
acc.ID = strconv.FormatInt(accounts[i].ID, 10)
balances, err := h.GetAccountBalance(acc.ID)
if h.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
resp, err := h.wsGetAccountsList()
if err != nil {
return info, err
}
var currencyDetails []exchange.AccountCurrencyInfo
for j := range balances {
var frozen bool
if balances[j].Type == "frozen" {
frozen = true
for i := range resp.Data {
if len(resp.Data[i].List) == 0 {
continue
}
currData := exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(resp.Data[i].List[0].Currency),
TotalValue: resp.Data[i].List[0].Balance,
}
if len(resp.Data[i].List) > 1 && resp.Data[i].List[1].Type == "frozen" {
currData.Hold = resp.Data[i].List[1].Balance
}
currencyDetails = append(currencyDetails, currData)
}
var acc exchange.Account
acc.Currencies = currencyDetails
info.Accounts = append(info.Accounts, acc)
} else {
accounts, err := h.GetAccountID()
if err != nil {
return info, err
}
for i := range accounts {
var acc exchange.Account
acc.ID = strconv.FormatInt(accounts[i].ID, 10)
balances, err := h.GetAccountBalance(acc.ID)
if err != nil {
return info, err
}
var updated bool
for i := range currencyDetails {
if currencyDetails[i].CurrencyName == currency.NewCode(balances[j].Currency) {
if frozen {
currencyDetails[i].Hold = balances[j].Balance
} else {
currencyDetails[i].TotalValue = balances[j].Balance
var currencyDetails []exchange.AccountCurrencyInfo
for j := range balances {
var frozen bool
if balances[j].Type == "frozen" {
frozen = true
}
var updated bool
for i := range currencyDetails {
if currencyDetails[i].CurrencyName == currency.NewCode(balances[j].Currency) {
if frozen {
currencyDetails[i].Hold = balances[j].Balance
} else {
currencyDetails[i].TotalValue = balances[j].Balance
}
updated = true
}
updated = true
}
if updated {
continue
}
if frozen {
currencyDetails = append(currencyDetails,
exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(balances[j].Currency),
Hold: balances[j].Balance,
})
} else {
currencyDetails = append(currencyDetails,
exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(balances[j].Currency),
TotalValue: balances[j].Balance,
})
}
}
if updated {
continue
}
if frozen {
currencyDetails = append(currencyDetails,
exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(balances[j].Currency),
Hold: balances[j].Balance,
})
} else {
currencyDetails = append(currencyDetails,
exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(balances[j].Currency),
TotalValue: balances[j].Balance,
})
}
acc.Currencies = currencyDetails
info.Accounts = append(info.Accounts, acc)
}
acc.Currencies = currencyDetails
info.Accounts = append(info.Accounts, acc)
}
return info, nil
}
@@ -498,13 +522,18 @@ func (h *HUOBI) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
params.Type = formattedType
response, err := h.SpotNewOrder(params)
if err != nil {
return submitOrderResponse, err
}
if response > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -527,9 +556,10 @@ func (h *HUOBI) CancelOrder(order *order.Cancel) error {
// CancelAllOrders cancels all orders associated with a currency pair
func (h *HUOBI) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
var cancelAllOrdersResponse order.CancelAllResponse
for _, currency := range h.GetEnabledPairs(asset.Spot) {
enabledPairs := h.GetEnabledPairs(asset.Spot)
for i := range enabledPairs {
resp, err := h.CancelOpenOrdersBatch(orderCancellation.AccountID,
h.FormatExchangeCurrency(currency, asset.Spot).String())
h.FormatExchangeCurrency(enabledPairs[i], asset.Spot).String())
if err != nil {
return cancelAllOrdersResponse, err
}
@@ -551,7 +581,56 @@ func (h *HUOBI) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAl
// GetOrderInfo returns information on a current open order
func (h *HUOBI) GetOrderInfo(orderID string) (order.Detail, error) {
var orderDetail order.Detail
return orderDetail, common.ErrNotYetImplemented
var respData *OrderInfo
if h.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
resp, err := h.wsGetOrderDetails(orderID)
if err != nil {
return orderDetail, err
}
respData = &resp.Data
} else {
oID, err := strconv.ParseInt(orderID, 10, 64)
if err != nil {
return orderDetail, err
}
resp, err := h.GetOrder(oID)
if err != nil {
return orderDetail, err
}
respData = &resp
}
if respData.ID == 0 {
return orderDetail, fmt.Errorf("%s - order not found for orderid %s", h.Name, orderID)
}
typeDetails := strings.Split(respData.Type, "-")
orderSide, err := order.StringToOrderSide(typeDetails[0])
if err != nil {
return orderDetail, err
}
orderType, err := order.StringToOrderType(typeDetails[1])
if err != nil {
return orderDetail, err
}
orderStatus, err := order.StringToOrderStatus(respData.State)
if err != nil {
return orderDetail, err
}
orderDetail = order.Detail{
Exchange: h.Name,
ID: strconv.FormatInt(respData.ID, 10),
AccountID: strconv.FormatInt(respData.AccountID, 10),
CurrencyPair: currency.NewPairFromString(respData.Symbol),
OrderType: orderType,
OrderSide: orderSide,
OrderDate: time.Unix(respData.CreatedAt, 0),
Status: orderStatus,
Price: respData.Price,
Amount: respData.Amount,
ExecutedAmount: respData.FilledAmount,
Fee: respData.FilledFees,
}
return orderDetail, nil
}
// GetDepositAddress returns a deposit address for a specified currency
@@ -607,32 +686,72 @@ func (h *HUOBI) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er
var orders []order.Detail
for i := range req.Currencies {
resp, err := h.GetOpenOrders(h.API.Credentials.ClientID,
req.Currencies[i].Lower().String(),
side,
500)
if err != nil {
return nil, err
if h.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
for i := range req.Currencies {
resp, err := h.wsGetOrdersList(-1, req.Currencies[i])
if err != nil {
return orders, err
}
for j := range resp.Data {
sideData := strings.Split(resp.Data[j].OrderState, "-")
side = sideData[0]
orderSide, err := order.StringToOrderSide(side)
if err != nil {
return orders, err
}
orderType, err := order.StringToOrderType(sideData[1])
if err != nil {
return orders, err
}
orderStatus, err := order.StringToOrderStatus(resp.Data[j].OrderState)
if err != nil {
return orders, err
}
orders = append(orders, order.Detail{
Exchange: h.Name,
AccountID: strconv.FormatInt(resp.Data[j].AccountID, 10),
ID: strconv.FormatInt(resp.Data[j].OrderID, 10),
CurrencyPair: req.Currencies[i],
OrderType: orderType,
OrderSide: orderSide,
OrderDate: time.Unix(resp.Data[j].CreatedAt, 0),
Status: orderStatus,
Price: resp.Data[j].Price,
Amount: resp.Data[j].OrderAmount,
ExecutedAmount: resp.Data[j].FilledAmount,
RemainingAmount: resp.Data[j].UnfilledAmount,
Fee: resp.Data[j].FilledFees,
})
}
}
for i := range resp {
orderDetail := order.Detail{
ID: strconv.FormatInt(int64(resp[i].ID), 10),
Price: resp[i].Price,
Amount: resp[i].Amount,
CurrencyPair: req.Currencies[i],
Exchange: h.Name,
ExecutedAmount: resp[i].FilledAmount,
OrderDate: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)),
Status: order.Status(resp[i].State),
AccountID: strconv.FormatFloat(resp[i].AccountID, 'f', -1, 64),
Fee: resp[i].FilledFees,
} else {
for i := range req.Currencies {
resp, err := h.GetOpenOrders(h.API.Credentials.ClientID,
req.Currencies[i].Lower().String(),
side,
500)
if err != nil {
return nil, err
}
setOrderSideAndType(resp[i].Type, &orderDetail)
for i := range resp {
orderDetail := order.Detail{
ID: strconv.FormatInt(resp[i].ID, 10),
Price: resp[i].Price,
Amount: resp[i].Amount,
CurrencyPair: req.Currencies[i],
Exchange: h.Name,
ExecutedAmount: resp[i].FilledAmount,
OrderDate: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)),
Status: order.Status(resp[i].State),
AccountID: strconv.FormatInt(resp[i].AccountID, 10),
Fee: resp[i].FilledFees,
}
orders = append(orders, orderDetail)
setOrderSideAndType(resp[i].Type, &orderDetail)
orders = append(orders, orderDetail)
}
}
}
@@ -664,7 +783,7 @@ func (h *HUOBI) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er
for i := range resp {
orderDetail := order.Detail{
ID: strconv.FormatInt(int64(resp[i].ID), 10),
ID: strconv.FormatInt(resp[i].ID, 10),
Price: resp[i].Price,
Amount: resp[i].Amount,
CurrencyPair: req.Currencies[i],
@@ -672,7 +791,7 @@ func (h *HUOBI) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er
ExecutedAmount: resp[i].FilledAmount,
OrderDate: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)),
Status: order.Status(resp[i].State),
AccountID: strconv.FormatFloat(resp[i].AccountID, 'f', -1, 64),
AccountID: strconv.FormatInt(resp[i].AccountID, 10),
Fee: resp[i].FilledFees,
}

View File

@@ -1,7 +1,9 @@
package itbit
import (
"log"
"net/url"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -21,19 +23,16 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
i.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Itbit load config error", err)
log.Fatal("Itbit load config error", err)
}
itbitConfig, err := cfg.GetExchangeConfig("ITBIT")
if err != nil {
t.Error("Itbit Setup() init error")
log.Fatal("Itbit Setup() init error")
}
itbitConfig.API.AuthenticatedSupport = true
itbitConfig.API.Credentials.Key = apiKey
@@ -42,8 +41,10 @@ func TestSetup(t *testing.T) {
err = i.Setup(itbitConfig)
if err != nil {
t.Fatal("Itbit setup error", err)
log.Fatal("Itbit setup error", err)
}
os.Exit(m.Run())
}
func TestGetTicker(t *testing.T) {
@@ -167,7 +168,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
i.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -255,20 +256,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
i.SetDefaults()
expectedResult := exchange.WithdrawCryptoViaWebsiteOnlyText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := i.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
i.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -282,9 +277,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
i.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -304,8 +296,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
i.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -330,15 +320,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
i.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -357,15 +343,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
i.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -388,7 +370,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestGetAccountInfo(t *testing.T) {
if apiKey != "" || apiSecret != "" || clientID != "" {
if areTestAPIKeysSet() {
_, err := i.GetAccountInfo()
if err == nil {
t.Error("GetAccountInfo() Expected error")
@@ -397,6 +379,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := i.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -404,8 +389,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
i.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -426,9 +409,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
i.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -441,9 +421,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
i.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}

View File

@@ -331,14 +331,18 @@ func (i *ItBit) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
s.Price,
s.Pair.String(),
"")
if err != nil {
return submitOrderResponse, err
}
if response.ID != "" {
submitOrderResponse.OrderID = response.ID
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
if response.AmountFilled == s.Amount {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -47,6 +47,7 @@ const (
krakenDepositAddresses = "DepositAddresses"
krakenWithdrawStatus = "WithdrawStatus"
krakenWithdrawCancel = "WithdrawCancel"
krakenWebsocketToken = "GetWebSocketsToken"
krakenAuthRate = 0
krakenUnauthRate = 0
@@ -57,8 +58,9 @@ var assetPairMap map[string]string
// Kraken is the overarching type across the alphapoint package
type Kraken struct {
exchange.Base
WebsocketConn *wshandler.WebsocketConnection
wsRequestMtx sync.Mutex
WebsocketConn *wshandler.WebsocketConnection
AuthenticatedWebsocketConn *wshandler.WebsocketConnection
wsRequestMtx sync.Mutex
}
// GetServerTime returns current server time
@@ -1036,3 +1038,14 @@ func (k *Kraken) WithdrawCancel(c currency.Code, refID string) (bool, error) {
return response.Result, GetError(response.Error)
}
func (k *Kraken) GetWebsocketToken() (string, error) {
var response WsTokenResponse
if err := k.SendAuthenticatedHTTPRequest(krakenWebsocketToken, url.Values{}, &response); err != nil {
return "", err
}
if len(response.Error) > 0 {
return "", fmt.Errorf("%s - %v", k.Name, response.Error)
}
return response.Result.Token, nil
}

View File

@@ -3,6 +3,8 @@ package kraken
import (
"log"
"net/http"
"os"
"strings"
"testing"
"github.com/gorilla/websocket"
@@ -25,13 +27,9 @@ const (
canManipulateRealOrders = false
)
// TestSetDefaults setup func
func TestSetDefaults(t *testing.T) {
k.SetDefaults()
}
// TestSetup setup func
func TestSetup(t *testing.T) {
func TestMain(m *testing.M) {
k.SetDefaults()
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
@@ -39,19 +37,19 @@ func TestSetup(t *testing.T) {
}
krakenConfig, err := cfg.GetExchangeConfig("Kraken")
if err != nil {
t.Error("kraken Setup() init error", err)
log.Fatal("kraken Setup() init error", err)
}
krakenConfig.API.AuthenticatedSupport = true
krakenConfig.API.Credentials.Key = apiKey
krakenConfig.API.Credentials.Secret = apiSecret
krakenConfig.API.Credentials.ClientID = clientID
krakenConfig.API.Endpoints.WebsocketURL = k.API.Endpoints.WebsocketURL
subscribeToDefaultChannels = false
err = k.Setup(krakenConfig)
if err != nil {
t.Fatal("Kraken setup error", err)
log.Fatal("Kraken setup error", err)
}
os.Exit(m.Run())
}
// TestGetServerTime API endpoint test
@@ -278,7 +276,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
k.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -290,8 +288,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
k.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
if areTestAPIKeysSet() {
@@ -373,11 +369,8 @@ func TestGetFee(t *testing.T) {
// TestFormatWithdrawPermissions logic test
func TestFormatWithdrawPermissions(t *testing.T) {
k.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " + exchange.AutoWithdrawFiatWithSetupText + " & " + exchange.WithdrawFiatWith2FAText
withdrawPermissions := k.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
@@ -385,9 +378,6 @@ func TestFormatWithdrawPermissions(t *testing.T) {
// TestGetActiveOrders wrapper test
func TestGetActiveOrders(t *testing.T) {
k.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -402,9 +392,6 @@ func TestGetActiveOrders(t *testing.T) {
// TestGetOrderHistory wrapper test
func TestGetOrderHistory(t *testing.T) {
k.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -417,6 +404,21 @@ func TestGetOrderHistory(t *testing.T) {
}
}
// TestGetOrderHistory wrapper test
func TestGetOrderInfo(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := k.GetOrderInfo("ImACoolOrderID")
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting error")
}
if areTestAPIKeysSet() && !strings.Contains(err.Error(), "- Order ID not found:") {
t.Error("Expected Order ID not found error")
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
@@ -425,9 +427,6 @@ func areTestAPIKeysSet() bool {
// TestSubmitOrder wrapper test
func TestSubmitOrder(t *testing.T) {
k.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -453,15 +452,11 @@ func TestSubmitOrder(t *testing.T) {
// TestCancelExchangeOrder wrapper test
func TestCancelExchangeOrder(t *testing.T) {
k.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -480,15 +475,11 @@ func TestCancelExchangeOrder(t *testing.T) {
// TestCancelAllExchangeOrders wrapper test
func TestCancelAllExchangeOrders(t *testing.T) {
k.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -512,7 +503,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
// TestGetAccountInfo wrapper test
func TestGetAccountInfo(t *testing.T) {
if apiKey != "" || apiSecret != "" || clientID != "" {
if areTestAPIKeysSet() || clientID != "" {
_, err := k.GetAccountInfo()
if err != nil {
t.Error("GetAccountInfo() error", err)
@@ -527,6 +518,9 @@ func TestGetAccountInfo(t *testing.T) {
// TestModifyOrder wrapper test
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := k.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -535,9 +529,6 @@ func TestModifyOrder(t *testing.T) {
// TestWithdraw wrapper test
func TestWithdraw(t *testing.T) {
k.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -563,9 +554,6 @@ func TestWithdraw(t *testing.T) {
// TestWithdrawFiat wrapper test
func TestWithdrawFiat(t *testing.T) {
k.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -590,9 +578,6 @@ func TestWithdrawFiat(t *testing.T) {
// TestWithdrawInternationalBank wrapper test
func TestWithdrawInternationalBank(t *testing.T) {
k.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -632,9 +617,6 @@ func TestGetDepositAddress(t *testing.T) {
// TestWithdrawStatus wrapper test
func TestWithdrawStatus(t *testing.T) {
k.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() {
_, err := k.WithdrawStatus(currency.BTC, "")
if err != nil {
@@ -650,8 +632,6 @@ func TestWithdrawStatus(t *testing.T) {
// TestWithdrawCancel wrapper test
func TestWithdrawCancel(t *testing.T) {
k.SetDefaults()
TestSetup(t)
_, err := k.WithdrawCancel(currency.BTC, "")
if areTestAPIKeysSet() && err == nil {
t.Error("WithdrawCancel() error cannot be nil")
@@ -666,12 +646,11 @@ func setupWsTests(t *testing.T) {
if wsSetupRan {
return
}
TestSetDefaults(t)
TestSetup(t)
if !k.Websocket.IsEnabled() && !k.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
k.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
comms = make(chan wshandler.WebsocketResponse, sharedtestvalues.WebsocketChannelOverrideCapacity)
k.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
k.WebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: k.Name,
@@ -680,12 +659,33 @@ func setupWsTests(t *testing.T) {
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
}
k.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: k.Name,
URL: krakenAuthWSURL,
Verbose: k.Verbose,
ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit,
ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout,
}
var dialer websocket.Dialer
err := k.WebsocketConn.Dial(&dialer, http.Header{})
if err != nil {
t.Fatal(err)
}
err = k.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{})
if err != nil {
t.Fatal(err)
}
token, err := k.GetWebsocketToken()
if err != nil {
t.Error(err)
}
authToken = token
go k.WsReadData(k.WebsocketConn)
go k.WsReadData(k.AuthenticatedWebsocketConn)
go k.WsHandleData()
go k.wsPingHandler()
wsSetupRan = true
}
@@ -700,3 +700,38 @@ func TestWebsocketSubscribe(t *testing.T) {
t.Error(err)
}
}
func TestGetWSToken(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("API keys required, skipping")
}
resp, err := k.GetWebsocketToken()
if err != nil {
t.Error(err)
}
if resp == "" {
t.Error("Token not returned")
}
}
func TestWsAddOrder(t *testing.T) {
setupWsTests(t)
_, err := k.wsAddOrder(&WsAddOrderRequest{
OrderType: order.Limit.Lower(),
OrderSide: order.Buy.Lower(),
Pair: "XBT/USD",
Price: -100,
})
if err != nil {
t.Error(err)
}
}
func TestWsCancelOrder(t *testing.T) {
setupWsTests(t)
err := k.wsCancelOrders([]string{"1337"})
if err != nil {
t.Error(err)
}
}

View File

@@ -1,6 +1,10 @@
package kraken
import "github.com/thrasher-corp/gocryptotrader/currency"
import (
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
)
// TimeResponse type
type TimeResponse struct {
@@ -391,7 +395,7 @@ type WithdrawStatusResponse struct {
type WebsocketSubscriptionEventRequest struct {
Event string `json:"event"` // subscribe
RequestID int64 `json:"reqid,omitempty"` // Optional, client originated ID reflected in response message.
Pairs []string `json:"pair"` // Array of currency pairs (pair1,pair2,pair3).
Pairs []string `json:"pair,omitempty"` // Array of currency pairs (pair1,pair2,pair3).
Subscription WebsocketSubscriptionData `json:"subscription,omitempty"`
}
@@ -413,6 +417,8 @@ type WebsocketSubscriptionData struct {
Name string `json:"name,omitempty"` // ticker|ohlc|trade|book|spread|*, * for all (ohlc interval value is 1 if all channels subscribed)
Interval int64 `json:"interval,omitempty"` // Optional - Time interval associated with ohlc subscription in minutes. Default 1. Valid Interval values: 1|5|15|30|60|240|1440|10080|21600
Depth int64 `json:"depth,omitempty"` // Optional - depth associated with book subscription in number of levels each side, default 10. Valid Options are: 10, 25, 100, 500, 1000
Token string `json:"token,omitempty"` // Optional used for authenticated requests
}
// WebsocketEventResponse holds all data response types
@@ -459,3 +465,103 @@ type WebsocketChannelData struct {
Pair currency.Pair
ChannelID int64
}
// WsTokenResponse holds the WS auth token
type WsTokenResponse struct {
Error []string `json:"error"`
Result struct {
Expires int64 `json:"expires"`
Token string `json:"token"`
} `json:"result"`
}
// WsOwnTrade ws auth owntrade data
type WsOwnTrade struct {
Cost float64 `json:"cost,string"`
Fee float64 `json:"fee,string"`
Margin float64 `json:"margin,string"`
OrderTransactionID string `json:"ordertxid"`
OrderType string `json:"ordertype"`
Pair string `json:"pair"`
PostTransactionID string `json:"postxid"`
Price float64 `json:"price,string"`
Time time.Time `json:"time"`
Type string `json:"type"`
Vol float64 `json:"vol,string"`
}
// WsOpenOrders ws auth open order data
type WsOpenOrders struct {
Cost float64 `json:"cost,string"`
Description WsOpenOrderDescription `json:"descr"`
ExpireTime time.Time `json:"expiretm"`
Fee float64 `json:"fee,string"`
LimitPrice float64 `json:"limitprice,string"`
Misc string `json:"misc"`
OFlags string `json:"oflags"`
OpenTime time.Time `json:"opentm"`
Price float64 `json:"price,string"`
RefID string `json:"refid"`
StartTime time.Time `json:"starttm"`
Status string `json:"status"`
StopPrice float64 `json:"stopprice,string"`
UserReference float64 `json:"userref"`
Volume float64 `json:"vol,string"`
ExecutedVolume float64 `json:"vol_exec,string"`
}
// WsOpenOrderDescription additional data for WsOpenOrders
type WsOpenOrderDescription struct {
Close string `json:"close"`
Leverage string `json:"leverage"`
Order string `json:"order"`
OrderType string `json:"ordertype"`
Pair string `json:"pair"`
Price float64 `json:"price,string"`
Price2 float64 `json:"price2,string"`
Type string `json:"type"`
}
// WsAddOrderRequest request type for ws adding order
type WsAddOrderRequest struct {
Event string `json:"event"`
Token string `json:"token"`
OrderType string `json:"ordertype"`
OrderSide string `json:"type"`
Pair string `json:"pair"`
Price float64 `json:"price,omitempty"` // optional
Price2 float64 `json:"price2,omitempty"` // optional
Volume float64 `json:"volume,omitempty"`
Leverage float64 `json:"leverage,omitempty"` // optional
OFlags string `json:"oflags,omitempty"` // optional
StartTime string `json:"starttm,omitempty"` // optional
ExpireTime string `json:"expiretm,omitempty"` // optional
UserReferenceID string `json:"userref,omitempty"` // optional
Validate string `json:"validate,omitempty"` // optional
CloseOrderType string `json:"close[ordertype],omitempty"` // optional
ClosePrice float64 `json:"close[price],omitempty"` // optional
ClosePrice2 float64 `json:"close[price2],omitempty"` // optional
}
// WsAddOrderResponse response data for ws order
type WsAddOrderResponse struct {
Description string `json:"descr"`
Event string `json:"event"`
Status string `json:"status"`
TransactionID string `json:"txid"`
ErrorMessage string `json:"errorMessage"`
}
// WsCancelOrderRequest request for ws cancel order
type WsCancelOrderRequest struct {
Event string `json:"event"`
Token string `json:"token"`
TransactionIDs []string `json:"txid"`
}
// WsCancelOrderResponse response data for ws cancel order
type WsCancelOrderResponse struct {
Event string `json:"event"`
Status string `json:"status"`
ErrorMessage string `json:"errorMessage"`
}

View File

@@ -10,7 +10,9 @@ import (
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"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/websocket/wshandler"
@@ -21,11 +23,9 @@ import (
// List of all websocket channels to subscribe to
const (
krakenWSURL = "wss://ws.kraken.com"
krakenAuthWSURL = "wss://ws-auth.kraken.com"
krakenWSSandboxURL = "wss://sandbox.kraken.com"
krakenWSSupportedVersion = "0.2.0"
// If a checksum fails, then resubscribing to the channel fails, fatal after these attempts
krakenWsResubscribeFailureLimit = 3
krakenWsResubscribeDelayInSeconds = 3
krakenWSSupportedVersion = "0.3.0"
// WS endpoints
krakenWsHeartbeat = "heartbeat"
krakenWsPing = "ping"
@@ -39,18 +39,22 @@ const (
krakenWsTrade = "trade"
krakenWsSpread = "spread"
krakenWsOrderbook = "book"
orderbookBufferLimit = 3
krakenWsRateLimit = 50
krakenWsOwnTrades = "ownTrades"
krakenWsOpenOrders = "openOrders"
krakenWsAddOrder = "addOrder"
krakenWsCancelOrder = "cancelOrder"
krakenWsRateLimit = 50
)
// orderbookMutex Ensures if two entries arrive at once, only one can be processed at a time
var subscriptionChannelPair []WebsocketChannelData
var subscribeToDefaultChannels = true
var comms = make(chan wshandler.WebsocketResponse)
var authToken string
// Channels require a topic and a currency
// Format [[ticker,but-t4u],[orderbook,nce-btt]]
var defaultSubscribedChannels = []string{krakenWsTicker, krakenWsTrade, krakenWsOrderbook, krakenWsOHLC, krakenWsSpread}
var authenticatedChannels = []string{krakenWsOwnTrades, krakenWsOpenOrders}
// WsConnect initiates a websocket connection
func (k *Kraken) WsConnect() error {
@@ -62,15 +66,86 @@ func (k *Kraken) WsConnect() error {
if err != nil {
return err
}
go k.WsHandleData()
go k.wsPingHandler()
if subscribeToDefaultChannels {
k.GenerateDefaultSubscriptions()
if k.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
authToken, err = k.GetWebsocketToken()
if err != nil {
k.Websocket.SetCanUseAuthenticatedEndpoints(false)
log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", k.Name, err)
}
err = k.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{})
if err != nil {
k.Websocket.SetCanUseAuthenticatedEndpoints(false)
log.Errorf(log.ExchangeSys, "%v - failed to connect to authenticated endpoint: %v\n", k.Name, err)
}
go k.WsReadData(k.AuthenticatedWebsocketConn)
k.GenerateAuthenticatedSubscriptions()
}
go k.WsReadData(k.WebsocketConn)
go k.WsHandleData()
go k.wsPingHandler()
k.GenerateDefaultSubscriptions()
return nil
}
// WsReadData funnels both auth and public ws data into one manageable place
func (k *Kraken) WsReadData(ws *wshandler.WebsocketConnection) {
k.Websocket.Wg.Add(1)
defer k.Websocket.Wg.Done()
for {
select {
case <-k.Websocket.ShutdownC:
return
default:
resp, err := ws.ReadMessage()
if err != nil {
k.Websocket.DataHandler <- err
return
}
k.Websocket.TrafficAlert <- struct{}{}
comms <- resp
}
}
}
// WsHandleData handles the read data from the websocket connection
func (k *Kraken) WsHandleData() {
k.Websocket.Wg.Add(1)
defer func() {
k.Websocket.Wg.Done()
}()
for {
select {
case <-k.Websocket.ShutdownC:
return
default:
resp := <-comms
// event response handling
var eventResponse WebsocketEventResponse
err := json.Unmarshal(resp.Raw, &eventResponse)
if err == nil && eventResponse.Event != "" {
k.WsHandleEventResponse(&eventResponse, resp.Raw)
continue
}
// Data response handling
var dataResponse WebsocketDataResponse
err = json.Unmarshal(resp.Raw, &dataResponse)
if err != nil {
log.Error(log.WebsocketMgr, fmt.Errorf("%s - unhandled websocket data: %v", k.Name, err))
continue
}
if _, ok := dataResponse[0].(float64); ok {
k.WsHandleDataResponse(dataResponse)
}
if _, ok := dataResponse[1].(string); ok {
k.wsHandleAuthDataResponse(dataResponse)
}
}
}
}
// wsPingHandler sends a message "ping" every 27 to maintain the connection to the websocket
func (k *Kraken) wsPingHandler() {
k.Websocket.Wg.Add(1)
@@ -96,82 +171,47 @@ func (k *Kraken) wsPingHandler() {
}
}
// WsHandleData handles the read data from the websocket connection
func (k *Kraken) WsHandleData() {
k.Websocket.Wg.Add(1)
defer func() {
k.Websocket.Wg.Done()
}()
for {
select {
case <-k.Websocket.ShutdownC:
return
default:
resp, err := k.WebsocketConn.ReadMessage()
if err != nil {
k.Websocket.ReadMessageErrors <- err
return
}
k.Websocket.TrafficAlert <- struct{}{}
// event response handling
var eventResponse WebsocketEventResponse
err = json.Unmarshal(resp.Raw, &eventResponse)
if err == nil && eventResponse.Event != "" {
k.WsHandleEventResponse(&eventResponse, resp.Raw)
continue
}
// Data response handling
var dataResponse WebsocketDataResponse
err = json.Unmarshal(resp.Raw, &dataResponse)
if err == nil && dataResponse[0].(float64) >= 0 {
k.WsHandleDataResponse(dataResponse)
continue
}
continue
}
}
}
// WsHandleDataResponse classifies the WS response and sends to appropriate handler
func (k *Kraken) WsHandleDataResponse(response WebsocketDataResponse) {
channelID := int64(response[0].(float64))
channelData := getSubscriptionChannelData(channelID)
switch channelData.Subscription {
case krakenWsTicker:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket ticker data received",
k.Name)
if cID, ok := response[0].(float64); ok {
channelID := int64(cID)
channelData := getSubscriptionChannelData(channelID)
switch channelData.Subscription {
case krakenWsTicker:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket ticker data received",
k.Name)
}
k.wsProcessTickers(&channelData, response[1].(map[string]interface{}))
case krakenWsOHLC:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket OHLC data received",
k.Name)
}
k.wsProcessCandles(&channelData, response[1].([]interface{}))
case krakenWsOrderbook:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket Orderbook data received",
k.Name)
}
k.wsProcessOrderBook(&channelData, response[1].(map[string]interface{}))
case krakenWsSpread:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket Spread data received",
k.Name)
}
k.wsProcessSpread(&channelData, response[1].([]interface{}))
case krakenWsTrade:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket Trade data received",
k.Name)
}
k.wsProcessTrades(&channelData, response[1].([]interface{}))
default:
log.Errorf(log.ExchangeSys, "%v Unidentified websocket data received: %v",
k.Name,
response)
}
k.wsProcessTickers(&channelData, response[1].(map[string]interface{}))
case krakenWsOHLC:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket OHLC data received",
k.Name)
}
k.wsProcessCandles(&channelData, response[1].([]interface{}))
case krakenWsOrderbook:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket Orderbook data received",
k.Name)
}
k.wsProcessOrderBook(&channelData, response[1].(map[string]interface{}))
case krakenWsSpread:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket Spread data received",
k.Name)
}
k.wsProcessSpread(&channelData, response[1].([]interface{}))
case krakenWsTrade:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket Trade data received",
k.Name)
}
k.wsProcessTrades(&channelData, response[1].([]interface{}))
default:
log.Errorf(log.ExchangeSys, "%v Unidentified websocket data received: %v",
k.Name,
response)
}
}
@@ -197,7 +237,7 @@ func (k *Kraken) WsHandleEventResponse(response *WebsocketEventResponse, rawResp
k.Websocket.DataHandler <- fmt.Errorf("%v Websocket status '%v'",
k.Name, response.Status)
}
if response.WebsocketStatusResponse.Version != krakenWSSupportedVersion {
if response.WebsocketStatusResponse.Version > krakenWSSupportedVersion {
log.Warnf(log.ExchangeSys, "%v New version of Websocket API released. Was %v Now %v",
k.Name, krakenWSSupportedVersion, response.WebsocketStatusResponse.Version)
}
@@ -214,6 +254,192 @@ func (k *Kraken) WsHandleEventResponse(response *WebsocketEventResponse, rawResp
}
}
func (k *Kraken) wsHandleAuthDataResponse(response WebsocketDataResponse) {
if chName, ok := response[1].(string); ok {
switch chName {
case krakenWsOwnTrades:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket auth own trade data received",
k.Name)
}
k.wsProcessOwnTrades(&response[0])
case krakenWsOpenOrders:
if k.Verbose {
log.Debugf(log.ExchangeSys, "%v Websocket auth open order data received",
k.Name)
}
k.wsProcessOpenOrders(&response[0])
}
}
}
func (k *Kraken) wsProcessOwnTrades(ownOrders interface{}) {
if data, ok := ownOrders.([]interface{}); ok {
for i := range data {
ownTrade := data[i].(map[string]interface{})
for _, val := range ownTrade {
tradeData := val.(map[string]interface{})
cost, err := strconv.ParseFloat(tradeData["cost"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
fee, err := strconv.ParseFloat(tradeData["fee"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
margin, err := strconv.ParseFloat(tradeData["margin"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
vol, err := strconv.ParseFloat(tradeData["vol"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
price, err := strconv.ParseFloat(tradeData["price"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
timeTogether, err := strconv.ParseFloat(tradeData["time"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
first, second, err := convert.SplitFloatDecimals(timeTogether)
if err != nil {
k.Websocket.DataHandler <- err
}
k.Websocket.DataHandler <- WsOwnTrade{
Cost: cost,
Fee: fee,
Margin: margin,
OrderTransactionID: tradeData["ordertxid"].(string),
OrderType: tradeData["ordertype"].(string),
Pair: tradeData["pair"].(string),
PostTransactionID: tradeData["postxid"].(string),
Price: price,
Time: time.Unix(first, second),
Type: tradeData["type"].(string),
Vol: vol,
}
}
}
} else {
k.Websocket.DataHandler <- errors.New(k.Name + " - Invalid own trades data")
}
}
func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) {
if data, ok := ownOrders.([]interface{}); ok {
for i := range data {
ownTrade := data[i].(map[string]interface{})
for key, val := range ownTrade {
tradeData := val.(map[string]interface{})
if len(tradeData) == 1 {
// just a status update
if status, ok := tradeData["status"].(string); ok {
k.Websocket.DataHandler <- k.Name + " - Order " + key + " " + status
}
}
startTimeConv, err := strconv.ParseFloat(tradeData["starttm"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
startTime, startTimeNano, err := convert.SplitFloatDecimals(startTimeConv)
if err != nil {
k.Websocket.DataHandler <- err
}
openTimeConv, err := strconv.ParseFloat(tradeData["opentm"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
openTime, openTimeNano, err := convert.SplitFloatDecimals(openTimeConv)
if err != nil {
k.Websocket.DataHandler <- err
}
expireTimeConv, err := strconv.ParseFloat(tradeData["expiretm"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
expireTime, expireTimeNano, err := convert.SplitFloatDecimals(expireTimeConv)
if err != nil {
k.Websocket.DataHandler <- err
}
cost, err := strconv.ParseFloat(tradeData["cost"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
executedVolume, err := strconv.ParseFloat(tradeData["vol_exec"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
volume, err := strconv.ParseFloat(tradeData["vol"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
userReference, err := strconv.ParseFloat(tradeData["userref"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
stopPrice, err := strconv.ParseFloat(tradeData["stopprice"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
price, err := strconv.ParseFloat(tradeData["price"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
limitPrice, err := strconv.ParseFloat(tradeData["limitprice"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
fee, err := strconv.ParseFloat(tradeData["fee"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
descriptionSubData := tradeData["description"].(map[string]interface{})
descriptionPrice, err := strconv.ParseFloat(descriptionSubData["price"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
descriptionPrice2, err := strconv.ParseFloat(descriptionSubData["price2"].(string), 64)
if err != nil {
k.Websocket.DataHandler <- err
}
description := WsOpenOrderDescription{
Close: descriptionSubData["close"].(string),
Leverage: descriptionSubData["leverage"].(string),
Order: descriptionSubData["order"].(string),
OrderType: descriptionSubData["ordertype"].(string),
Pair: descriptionSubData["pair"].(string),
Price: descriptionPrice,
Price2: descriptionPrice2,
Type: descriptionSubData["type"].(string),
}
k.Websocket.DataHandler <- WsOpenOrders{
Cost: cost,
ExpireTime: time.Unix(expireTime, expireTimeNano),
Description: description,
Fee: fee,
LimitPrice: limitPrice,
Misc: tradeData["misc"].(string),
OFlags: tradeData["oflags"].(string),
OpenTime: time.Unix(openTime, openTimeNano),
Price: price,
RefID: tradeData["refid"].(string),
StartTime: time.Unix(startTime, startTimeNano),
Status: tradeData["status"].(string),
StopPrice: stopPrice,
UserReference: userReference,
Volume: volume,
ExecutedVolume: executedVolume,
}
}
}
} else {
k.Websocket.DataHandler <- errors.New(k.Name + " - Invalid own trades data")
}
}
// addNewSubscriptionChannelData stores channel ids, pairs and subscription types to an array
// allowing correlation between subscriptions and returned data
func addNewSubscriptionChannelData(response *WebsocketEventResponse) {
@@ -623,22 +849,39 @@ func (k *Kraken) GenerateDefaultSubscriptions() {
k.Websocket.SubscribeToChannels(subscriptions)
}
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
func (k *Kraken) GenerateAuthenticatedSubscriptions() {
var subscriptions []wshandler.WebsocketChannelSubscription
for i := range authenticatedChannels {
params := make(map[string]interface{})
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
Channel: authenticatedChannels[i],
Params: params,
})
}
k.Websocket.SubscribeToChannels(subscriptions)
}
// Subscribe sends a websocket message to receive data from the channel
func (k *Kraken) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
var depth int64
if channelToSubscribe.Channel == "book" {
depth = 1000
}
resp := WebsocketSubscriptionEventRequest{
Event: krakenWsSubscribe,
Pairs: []string{channelToSubscribe.Currency.String()},
Subscription: WebsocketSubscriptionData{
Name: channelToSubscribe.Channel,
Depth: depth,
Name: channelToSubscribe.Channel,
},
RequestID: k.WebsocketConn.GenerateMessageID(false),
}
if channelToSubscribe.Channel == "book" {
// TODO: Add ability to make depth customisable
resp.Subscription.Depth = 1000
}
if !channelToSubscribe.Currency.IsEmpty() {
resp.Pairs = []string{channelToSubscribe.Currency.String()}
}
if channelToSubscribe.Params != nil {
resp.Subscription.Token = authToken
}
_, err := k.WebsocketConn.SendMessageReturnResponse(resp.RequestID, resp)
return err
}
@@ -656,3 +899,32 @@ func (k *Kraken) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscr
_, err := k.WebsocketConn.SendMessageReturnResponse(resp.RequestID, resp)
return err
}
func (k *Kraken) wsAddOrder(request *WsAddOrderRequest) (string, error) {
id := k.AuthenticatedWebsocketConn.GenerateMessageID(false)
request.UserReferenceID = strconv.FormatInt(id, 10)
request.Event = krakenWsAddOrder
request.Token = authToken
jsonResp, err := k.AuthenticatedWebsocketConn.SendMessageReturnResponse(id, request)
if err != nil {
return "", err
}
var resp WsAddOrderResponse
err = json.Unmarshal(jsonResp, &resp)
if err != nil {
return "", err
}
if resp.ErrorMessage != "" {
return "", fmt.Errorf(k.Name + " - " + resp.ErrorMessage)
}
return resp.TransactionID, nil
}
func (k *Kraken) wsCancelOrders(orderIDs []string) error {
request := WsCancelOrderRequest{
Event: krakenWsCancelOrder,
Token: authToken,
TransactionIDs: orderIDs,
}
return k.AuthenticatedWebsocketConn.SendMessage(request)
}

View File

@@ -9,6 +9,7 @@ import (
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -106,6 +107,9 @@ func (k *Kraken) SetDefaults() {
Subscribe: true,
Unsubscribe: true,
MessageCorrelation: true,
SubmitOrder: true,
CancelOrder: true,
CancelOrders: true,
},
WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup |
exchange.WithdrawCryptoWith2FA |
@@ -171,6 +175,16 @@ func (k *Kraken) Setup(exch *config.ExchangeConfig) error {
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
k.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{
ExchangeName: k.Name,
URL: krakenAuthWSURL,
ProxyURL: k.Websocket.GetProxyAddress(),
Verbose: k.Verbose,
RateLimit: krakenWsRateLimit,
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
}
k.Websocket.Orderbook.Setup(
exch.WebsocketOrderbookBufferLimit,
true,
@@ -397,25 +411,47 @@ func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]ex
// SubmitOrder submits a new order
func (k *Kraken) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
err := s.Validate()
if err != nil {
return submitOrderResponse, err
}
response, err := k.AddOrder(s.Pair.String(),
s.OrderSide.String(),
s.OrderType.String(),
s.Amount,
s.Price,
0,
0,
&AddOrderOptions{})
if len(response.TransactionIds) > 0 {
submitOrderResponse.OrderID = strings.Join(response.TransactionIds, ", ")
}
if err == nil {
if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var resp string
resp, err = k.wsAddOrder(&WsAddOrderRequest{
OrderType: s.OrderType.String(),
OrderSide: s.OrderSide.String(),
Pair: s.Pair.String(),
Price: s.Price,
Volume: s.Amount,
})
if err != nil {
return submitOrderResponse, err
}
submitOrderResponse.OrderID = resp
submitOrderResponse.IsOrderPlaced = true
} else {
var response AddOrderResponse
response, err = k.AddOrder(s.Pair.String(),
s.OrderSide.String(),
s.OrderType.String(),
s.Amount,
s.Price,
0,
0,
&AddOrderOptions{})
if err != nil {
return submitOrderResponse, err
}
if len(response.TransactionIds) > 0 {
submitOrderResponse.OrderID = strings.Join(response.TransactionIds, ", ")
}
}
return submitOrderResponse, err
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -426,6 +462,9 @@ func (k *Kraken) ModifyOrder(action *order.Modify) (string, error) {
// CancelOrder cancels an order by its corresponding ID number
func (k *Kraken) CancelOrder(order *order.Cancel) error {
if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
return k.wsCancelOrders([]string{order.OrderID})
}
_, err := k.CancelExistingOrder(order.OrderID)
return err
@@ -436,26 +475,78 @@ func (k *Kraken) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, erro
cancelAllOrdersResponse := order.CancelAllResponse{
Status: make(map[string]string),
}
var emptyOrderOptions OrderInfoOptions
openOrders, err := k.GetOpenOrders(emptyOrderOptions)
if err != nil {
return cancelAllOrdersResponse, err
}
for orderID := range openOrders.Open {
_, err = k.CancelExistingOrder(orderID)
var err error
if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
err = k.wsCancelOrders([]string{orderID})
} else {
_, err = k.CancelExistingOrder(orderID)
}
if err != nil {
cancelAllOrdersResponse.Status[orderID] = err.Error()
}
}
return cancelAllOrdersResponse, nil
}
// GetOrderInfo returns information on a current open order
func (k *Kraken) GetOrderInfo(orderID string) (order.Detail, error) {
var orderDetail order.Detail
return orderDetail, common.ErrNotYetImplemented
var emptyOrderOptions OrderInfoOptions
openOrders, err := k.GetOpenOrders(emptyOrderOptions)
if err != nil {
return orderDetail, err
}
if orderInfo, ok := openOrders.Open[orderID]; ok {
var trades []order.TradeHistory
for i := range orderInfo.Trades {
trades = append(trades, order.TradeHistory{
TID: orderInfo.Trades[i],
})
}
firstNum, decNum, err := convert.SplitFloatDecimals(orderInfo.StartTime)
if err != nil {
return orderDetail, err
}
side, err := order.StringToOrderSide(orderInfo.Description.Type)
if err != nil {
return orderDetail, err
}
status, err := order.StringToOrderStatus(orderInfo.Status)
if err != nil {
return orderDetail, err
}
oType, err := order.StringToOrderType(orderInfo.Description.OrderType)
if err != nil {
return orderDetail, err
}
orderDetail = order.Detail{
Exchange: k.Name,
ID: orderID,
CurrencyPair: currency.NewPairFromString(orderInfo.Description.Pair),
OrderSide: side,
OrderType: oType,
OrderDate: time.Unix(firstNum, decNum),
Status: status,
Price: orderInfo.Price,
Amount: orderInfo.Volume,
ExecutedAmount: orderInfo.VolumeExecuted,
RemainingAmount: orderInfo.Volume - orderInfo.VolumeExecuted,
Fee: orderInfo.Fee,
Trades: trades,
}
} else {
return orderDetail, errors.New(k.Name + " - Order ID not found: " + orderID)
}
return orderDetail, nil
}
// GetDepositAddress returns a deposit address for a specified currency
@@ -606,5 +697,9 @@ func (k *Kraken) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, e
// AuthenticateWebsocket sends an authentication message to the websocket
func (k *Kraken) AuthenticateWebsocket() error {
return common.ErrFunctionNotSupported
resp, err := k.GetWebsocketToken()
if resp != "" {
authToken = resp
}
return err
}

View File

@@ -3,6 +3,7 @@ package lakebtc
import (
"fmt"
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -16,7 +17,6 @@ import (
)
var l LakeBTC
var setupRan bool
// Please add your own APIkeys to do correct due diligence testing.
const (
@@ -25,34 +25,28 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
if !setupRan {
l.SetDefaults()
func TestMain(m *testing.M) {
l.SetDefaults()
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("LakeBTC load config error", err)
}
}
lakebtcConfig, err := cfg.GetExchangeConfig("LakeBTC")
if err != nil {
log.Fatal("LakeBTC Setup() init error")
}
lakebtcConfig.API.AuthenticatedSupport = true
lakebtcConfig.API.Credentials.Key = apiKey
lakebtcConfig.API.Credentials.Secret = apiSecret
lakebtcConfig.Features.Enabled.Websocket = true
err = l.Setup(lakebtcConfig)
if err != nil {
log.Fatal("LakeBTC setup error", err)
}
l.API.Endpoints.WebsocketURL = lakeBTCWSURL
func TestSetup(t *testing.T) {
if !setupRan {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("LakeBTC load config error", err)
}
lakebtcConfig, err := cfg.GetExchangeConfig("LakeBTC")
if err != nil {
t.Error("LakeBTC Setup() init error")
}
lakebtcConfig.API.AuthenticatedSupport = true
lakebtcConfig.API.Credentials.Key = apiKey
lakebtcConfig.API.Credentials.Secret = apiSecret
lakebtcConfig.Features.Enabled.Websocket = true
err = l.Setup(lakebtcConfig)
if err != nil {
t.Fatal("LakeBTC setup error", err)
}
l.API.Endpoints.WebsocketURL = lakeBTCWSURL
setupRan = true
}
os.Exit(m.Run())
}
func TestFetchTradablePairs(t *testing.T) {
@@ -171,7 +165,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
l.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -259,20 +253,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
l.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := l.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
l.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -286,9 +274,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
l.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
}
@@ -308,9 +293,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -335,15 +317,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -361,15 +339,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -392,6 +366,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := l.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -399,8 +376,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
l.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -424,9 +399,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -439,9 +411,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -469,8 +438,6 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsConn websocket connection test
func TestWsConn(t *testing.T) {
TestSetDefaults(t)
TestSetup(t)
if !l.Websocket.IsEnabled() {
t.Skip(wshandler.WebsocketNotEnabled)
}
@@ -484,8 +451,6 @@ func TestWsConn(t *testing.T) {
// TestWsTradeProcessing logic test
func TestWsTradeProcessing(t *testing.T) {
TestSetDefaults(t)
TestSetup(t)
l.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
l.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
json := `{"trades":[{"type":"sell","date":1564985787,"price":"11913.02","amount":"0.49"}]}`
@@ -497,8 +462,6 @@ func TestWsTradeProcessing(t *testing.T) {
// TestWsTickerProcessing logic test
func TestWsTickerProcessing(t *testing.T) {
TestSetDefaults(t)
TestSetup(t)
const testChanSize = 26
l.Websocket.DataHandler = make(chan interface{}, testChanSize)
l.Websocket.TrafficAlert = make(chan struct{}, testChanSize)
@@ -519,8 +482,6 @@ func TestGetCurrencyFromChannel(t *testing.T) {
// TestWsOrderbookProcessing logic test
func TestWsOrderbookProcessing(t *testing.T) {
TestSetDefaults(t)
TestSetup(t)
l.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
l.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
json := `{"asks":[["11905.66","0.0019"],["11905.73","0.0015"],["11906.43","0.0013"],["11906.62","0.0019"],["11907.25","11.087"],["11907.66","0.0006"],["11907.73","0.3113"],["11907.84","0.0006"],["11908.37","0.0016"],["11908.86","10.3786"],["11909.54","4.2955"],["11910.15","0.0012"],["11910.56","13.5505"],["11911.06","0.0011"],["11911.37","0.0023"]],"bids":[["11905.55","0.0171"],["11904.43","0.0225"],["11903.31","0.0223"],["11902.2","0.0027"],["11901.92","1.002"],["11901.6","0.0015"],["11901.49","0.0012"],["11901.08","0.0227"],["11900.93","0.0009"],["11900.53","1.662"],["11900.08","0.001"],["11900.01","3.6745"],["11899.96","0.003"],["11899.91","0.0006"],["11899.44","0.0013"]]}`

View File

@@ -334,13 +334,18 @@ func (l *LakeBTC) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
isBuyOrder := s.OrderSide == order.Buy
response, err := l.Trade(isBuyOrder, s.Amount, s.Price,
s.Pair.Lower().String())
if err != nil {
return submitOrderResponse, err
}
if response.ID > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -369,8 +374,8 @@ func (l *LakeBTC) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, err
}
var ordersToCancel []string
for _, order := range openOrders {
ordersToCancel = append(ordersToCancel, strconv.FormatInt(order.ID, 10))
for i := range openOrders {
ordersToCancel = append(ordersToCancel, strconv.FormatInt(openOrders[i].ID, 10))
}
return cancelAllOrdersResponse, l.CancelExistingOrders(ordersToCancel)

View File

@@ -1,9 +1,9 @@
package lbank
import (
"fmt"
"log"
"os"
"strconv"
"testing"
"time"
@@ -91,7 +91,7 @@ func TestGetMarketDepths(t *testing.T) {
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := l.GetTrades(testCurrencyPair, "600",
fmt.Sprintf("%v", time.Now().Unix()))
strconv.FormatInt(time.Now().Unix(), 10))
if err != nil {
t.Error(err)
}
@@ -104,7 +104,7 @@ func TestGetTrades(t *testing.T) {
func TestGetKlines(t *testing.T) {
t.Parallel()
_, err := l.GetKlines(testCurrencyPair, "600", "minute1",
fmt.Sprintf("%v", time.Now().Unix()))
strconv.FormatInt(time.Now().Unix(), 10))
if err != nil {
t.Error(err)
}

View File

@@ -315,6 +315,9 @@ func (l *Lbank) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
}
resp.IsOrderPlaced = true
resp.OrderID = tempResp.OrderID
if s.OrderType == order.Market {
resp.FullyMatched = true
}
return resp, nil
}

View File

@@ -579,7 +579,7 @@ func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) er
path := localbitcoinsAPIWalletSend
if pin > 0 {
values.Set("pincode", fmt.Sprintf("%v", pin))
values.Set("pincode", strconv.FormatInt(pin, 10))
path = localbitcoinsAPIWalletSendPin
}

View File

@@ -96,7 +96,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
l.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -189,7 +189,6 @@ func TestFormatWithdrawPermissions(t *testing.T) {
expectedResult := exchange.AutoWithdrawCryptoText +
" & " +
exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := l.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s",
@@ -275,7 +274,6 @@ func TestCancelExchangeOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -300,7 +298,6 @@ func TestCancelAllExchangeOrders(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",

View File

@@ -2,7 +2,9 @@ package okcoin
import (
"encoding/json"
"log"
"net/http"
"os"
"strings"
"sync"
"testing"
@@ -34,43 +36,25 @@ var testSetupRan bool
var spotCurrency = currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Lower().String()
var websocketEnabled bool
// TestSetDefaults Sets standard default settings for running a test
func TestSetDefaults(t *testing.T) {
if o.Name != OKGroupExchange {
o.SetDefaults()
}
if o.Name != OKGroupExchange {
t.Errorf("%v - SetDefaults() error", OKGroupExchange)
}
TestSetup(t)
}
// TestSetRealOrderDefaults Sets test defaults when test can impact real money/orders
func TestSetRealOrderDefaults(t *testing.T) {
TestSetDefaults(t)
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("Ensure canManipulateRealOrders is true and your API keys are set")
}
}
// TestSetup Sets defaults for test environment
func TestSetup(t *testing.T) {
if testSetupRan {
return
}
if o.API.Credentials.Key == apiKey && o.API.Credentials.Secret == apiSecret &&
o.API.Credentials.ClientID == passphrase && apiKey != "" && apiSecret != "" && passphrase != "" {
return
}
func TestMain(m *testing.M) {
o.SetDefaults()
o.ExchangeName = OKGroupExchange
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Okcoin load config error", err)
log.Fatal("Okcoin load config error", err)
}
okcoinConfig, err := cfg.GetExchangeConfig(OKGroupExchange)
if err != nil {
t.Fatalf("%v Setup() init error", OKGroupExchange)
log.Fatalf("%v Setup() init error", OKGroupExchange)
}
if okcoinConfig.Features.Enabled.Websocket {
websocketEnabled = true
@@ -84,11 +68,13 @@ func TestSetup(t *testing.T) {
okcoinConfig.API.Endpoints.WebsocketURL = o.API.Endpoints.WebsocketURL
err = o.Setup(okcoinConfig)
if err != nil {
t.Fatal("OKCoin setup error", err)
log.Fatal("OKCoin setup error", err)
}
testSetupRan = true
o.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
o.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
os.Exit(m.Run())
}
func areTestAPIKeysSet() bool {
@@ -106,14 +92,12 @@ func testStandardErrorHandling(t *testing.T, err error) {
// TestGetAccountCurrencies API endpoint test
func TestGetAccountCurrencies(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetAccountCurrencies()
testStandardErrorHandling(t, err)
}
// TestGetAccountWalletInformation API endpoint test
func TestGetAccountWalletInformation(t *testing.T) {
TestSetDefaults(t)
resp, err := o.GetAccountWalletInformation("")
if areTestAPIKeysSet() {
if err != nil {
@@ -129,7 +113,6 @@ func TestGetAccountWalletInformation(t *testing.T) {
// TestGetAccountWalletInformationForCurrency API endpoint test
func TestGetAccountWalletInformationForCurrency(t *testing.T) {
TestSetDefaults(t)
resp, err := o.GetAccountWalletInformation(currency.BTC.String())
if areTestAPIKeysSet() {
if err != nil {
@@ -173,7 +156,6 @@ func TestAccountWithdrawRequest(t *testing.T) {
// TestGetAccountWithdrawalFee API endpoint test
func TestGetAccountWithdrawalFee(t *testing.T) {
TestSetDefaults(t)
resp, err := o.GetAccountWithdrawalFee("")
if areTestAPIKeysSet() {
if err != nil {
@@ -189,7 +171,6 @@ func TestGetAccountWithdrawalFee(t *testing.T) {
// TestGetWithdrawalFeeForCurrency API endpoint test
func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) {
TestSetDefaults(t)
resp, err := o.GetAccountWithdrawalFee(currency.BTC.String())
if areTestAPIKeysSet() {
if err != nil {
@@ -205,63 +186,54 @@ func TestGetAccountWithdrawalFeeForCurrency(t *testing.T) {
// TestGetAccountWithdrawalHistory API endpoint test
func TestGetAccountWithdrawalHistory(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetAccountWithdrawalHistory("")
testStandardErrorHandling(t, err)
}
// TestGetAccountWithdrawalHistoryForCurrency API endpoint test
func TestGetAccountWithdrawalHistoryForCurrency(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetAccountWithdrawalHistory(currency.BTC.String())
testStandardErrorHandling(t, err)
}
// TestGetAccountBillDetails API endpoint test
func TestGetAccountBillDetails(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetAccountBillDetails(okgroup.GetAccountBillDetailsRequest{})
testStandardErrorHandling(t, err)
}
// TestGetAccountDepositAddressForCurrency API endpoint test
func TestGetAccountDepositAddressForCurrency(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetAccountDepositAddressForCurrency(currency.BTC.String())
testStandardErrorHandling(t, err)
}
// TestGetAccountDepositHistory API endpoint test
func TestGetAccountDepositHistory(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetAccountDepositHistory("")
testStandardErrorHandling(t, err)
}
// TestGetAccountDepositHistoryForCurrency API endpoint test
func TestGetAccountDepositHistoryForCurrency(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetAccountDepositHistory(currency.BTC.String())
testStandardErrorHandling(t, err)
}
// TestGetSpotTradingAccounts API endpoint test
func TestGetSpotTradingAccounts(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetSpotTradingAccounts()
testStandardErrorHandling(t, err)
}
// TestGetSpotTradingAccountsForCurrency API endpoint test
func TestGetSpotTradingAccountsForCurrency(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetSpotTradingAccountForCurrency(currency.BTC.String())
testStandardErrorHandling(t, err)
}
// TestGetSpotBillDetailsForCurrency API endpoint test
func TestGetSpotBillDetailsForCurrency(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotBillDetailsForCurrencyRequest{
Currency: currency.BTC.String(),
Limit: 100,
@@ -272,7 +244,6 @@ func TestGetSpotBillDetailsForCurrency(t *testing.T) {
// TestGetSpotBillDetailsForCurrencyBadLimit API logic test
func TestGetSpotBillDetailsForCurrencyBadLimit(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotBillDetailsForCurrencyRequest{
Currency: currency.BTC.String(),
Limit: -1,
@@ -316,7 +287,7 @@ func TestPlaceSpotOrderMarket(t *testing.T) {
// TestPlaceMultipleSpotOrders API endpoint test
func TestPlaceMultipleSpotOrders(t *testing.T) {
TestSetRealOrderDefaults(t)
order := okgroup.PlaceOrderRequest{
ord := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
@@ -325,7 +296,7 @@ func TestPlaceMultipleSpotOrders(t *testing.T) {
}
request := []okgroup.PlaceOrderRequest{
order,
ord,
}
_, errs := o.PlaceMultipleSpotOrders(request)
@@ -336,8 +307,7 @@ func TestPlaceMultipleSpotOrders(t *testing.T) {
// TestPlaceMultipleSpotOrdersOverCurrencyLimits API logic test
func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceOrderRequest{
ord := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
@@ -346,11 +316,11 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
}
request := []okgroup.PlaceOrderRequest{
order,
order,
order,
order,
order,
ord,
ord,
ord,
ord,
ord,
}
_, errs := o.PlaceMultipleSpotOrders(request)
@@ -361,8 +331,7 @@ func TestPlaceMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
// TestPlaceMultipleSpotOrdersOverPairLimits API logic test
func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceOrderRequest{
ord := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
@@ -371,7 +340,7 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
}
request := []okgroup.PlaceOrderRequest{
order,
ord,
}
pairs := currency.Pairs{
@@ -382,8 +351,8 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
}
for x := range pairs {
order.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, order)
ord.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, ord)
}
_, errs := o.PlaceMultipleSpotOrders(request)
@@ -439,7 +408,6 @@ func TestCancelMultipleSpotOrdersOverCurrencyLimits(t *testing.T) {
// TestGetSpotOrders API endpoint test
func TestGetSpotOrders(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotOrdersRequest{
InstrumentID: spotCurrency,
Status: "all",
@@ -450,7 +418,6 @@ func TestGetSpotOrders(t *testing.T) {
// TestGetSpotOpenOrders API endpoint test
func TestGetSpotOpenOrders(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotOpenOrdersRequest{}
_, err := o.GetSpotOpenOrders(request)
testStandardErrorHandling(t, err)
@@ -458,7 +425,6 @@ func TestGetSpotOpenOrders(t *testing.T) {
// TestGetSpotOrder API endpoint test
func TestGetSpotOrder(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotOrderRequest{
OrderID: "-1234",
InstrumentID: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Upper().String(),
@@ -469,7 +435,6 @@ func TestGetSpotOrder(t *testing.T) {
// TestGetSpotTransactionDetails API endpoint test
func TestGetSpotTransactionDetails(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotTransactionDetailsRequest{
OrderID: 1234,
InstrumentID: spotCurrency,
@@ -480,7 +445,6 @@ func TestGetSpotTransactionDetails(t *testing.T) {
// TestGetSpotTokenPairDetails API endpoint test
func TestGetSpotTokenPairDetails(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetSpotTokenPairDetails()
if err != nil {
t.Error(err)
@@ -489,7 +453,6 @@ func TestGetSpotTokenPairDetails(t *testing.T) {
// TestGetSpotAllTokenPairsInformation API endpoint test
func TestGetSpotAllTokenPairsInformation(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetSpotAllTokenPairsInformation()
if err != nil {
t.Error(err)
@@ -498,7 +461,6 @@ func TestGetSpotAllTokenPairsInformation(t *testing.T) {
// TestGetSpotAllTokenPairsInformationForCurrency API endpoint test
func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetSpotAllTokenPairsInformationForCurrency(spotCurrency)
if err != nil {
t.Error(err)
@@ -507,7 +469,6 @@ func TestGetSpotAllTokenPairsInformationForCurrency(t *testing.T) {
// TestGetSpotFilledOrdersInformation API endpoint test
func TestGetSpotFilledOrdersInformation(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotFilledOrdersInformationRequest{
InstrumentID: spotCurrency,
}
@@ -519,7 +480,6 @@ func TestGetSpotFilledOrdersInformation(t *testing.T) {
// TestGetSpotMarketData API endpoint test
func TestGetSpotMarketData(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotMarketDataRequest{
InstrumentID: spotCurrency,
Granularity: 604800,
@@ -532,21 +492,18 @@ func TestGetSpotMarketData(t *testing.T) {
// TestGetMarginTradingAccounts API endpoint test
func TestGetMarginTradingAccounts(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetMarginTradingAccounts()
testStandardErrorHandling(t, err)
}
// TestGetMarginTradingAccountsForCurrency API endpoint test
func TestGetMarginTradingAccountsForCurrency(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetMarginTradingAccountsForCurrency(spotCurrency)
testStandardErrorHandling(t, err)
}
// TestGetMarginBillDetails API endpoint test
func TestGetMarginBillDetails(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetMarginBillDetailsRequest{
InstrumentID: spotCurrency,
Limit: 100,
@@ -557,14 +514,12 @@ func TestGetMarginBillDetails(t *testing.T) {
// TestGetMarginAccountSettings API endpoint test
func TestGetMarginAccountSettings(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetMarginAccountSettings("")
testStandardErrorHandling(t, err)
}
// TestGetMarginAccountSettingsForCurrency API endpoint test
func TestGetMarginAccountSettingsForCurrency(t *testing.T) {
TestSetDefaults(t)
_, err := o.GetMarginAccountSettings(spotCurrency)
testStandardErrorHandling(t, err)
}
@@ -631,7 +586,7 @@ func TestPlaceMarginOrderMarket(t *testing.T) {
// TestPlaceMultipleMarginOrders API endpoint test
func TestPlaceMultipleMarginOrders(t *testing.T) {
TestSetRealOrderDefaults(t)
order := okgroup.PlaceOrderRequest{
ord := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
@@ -641,7 +596,7 @@ func TestPlaceMultipleMarginOrders(t *testing.T) {
}
request := []okgroup.PlaceOrderRequest{
order,
ord,
}
_, errs := o.PlaceMultipleMarginOrders(request)
@@ -652,8 +607,7 @@ func TestPlaceMultipleMarginOrders(t *testing.T) {
// TestPlaceMultipleMarginOrdersOverCurrencyLimits API logic test
func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceOrderRequest{
ord := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
@@ -663,11 +617,11 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
}
request := []okgroup.PlaceOrderRequest{
order,
order,
order,
order,
order,
ord,
ord,
ord,
ord,
ord,
}
_, errs := o.PlaceMultipleMarginOrders(request)
@@ -678,8 +632,7 @@ func TestPlaceMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
// TestPlaceMultipleMarginOrdersOverPairLimits API logic test
func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
TestSetDefaults(t)
order := okgroup.PlaceOrderRequest{
ord := okgroup.PlaceOrderRequest{
InstrumentID: spotCurrency,
Type: order.Limit.Lower(),
Side: order.Buy.Lower(),
@@ -689,7 +642,7 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
}
request := []okgroup.PlaceOrderRequest{
order,
ord,
}
pairs := currency.Pairs{
@@ -700,8 +653,8 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
}
for x := range pairs {
order.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, order)
ord.InstrumentID = pairs[x].Format("-", false).String()
request = append(request, ord)
}
_, errs := o.PlaceMultipleMarginOrders(request)
@@ -752,7 +705,6 @@ func TestCancelMultipleMarginOrdersOverCurrencyLimits(t *testing.T) {
// TestGetMarginOrders API endpoint test
func TestGetMarginOrders(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotOrdersRequest{
InstrumentID: spotCurrency,
Status: "all",
@@ -763,7 +715,6 @@ func TestGetMarginOrders(t *testing.T) {
// TestGetMarginOpenOrders API endpoint test
func TestGetMarginOpenOrders(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotOpenOrdersRequest{}
_, err := o.GetMarginOpenOrders(request)
testStandardErrorHandling(t, err)
@@ -771,7 +722,6 @@ func TestGetMarginOpenOrders(t *testing.T) {
// TestGetMarginOrder API endpoint test
func TestGetMarginOrder(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotOrderRequest{
OrderID: "1234",
InstrumentID: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USD.String(), "-").Upper().String(),
@@ -782,7 +732,6 @@ func TestGetMarginOrder(t *testing.T) {
// TestGetMarginTransactionDetails API endpoint test
func TestGetMarginTransactionDetails(t *testing.T) {
TestSetDefaults(t)
request := okgroup.GetSpotTransactionDetailsRequest{
OrderID: 1234,
InstrumentID: spotCurrency,
@@ -797,7 +746,6 @@ func TestGetMarginTransactionDetails(t *testing.T) {
// Attempts to subscribe to a channel that doesn't exist
// Will log in if credentials are present
func TestSendWsMessages(t *testing.T) {
TestSetDefaults(t)
if !o.Websocket.IsEnabled() && !o.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
@@ -850,7 +798,6 @@ func TestGetAssetTypeFromTableName(t *testing.T) {
// TestGetWsChannelWithoutOrderType logic test
func TestGetWsChannelWithoutOrderType(t *testing.T) {
TestSetDefaults(t)
str := "spot/depth5:BTC-USDT"
expected := "depth5"
resp := o.GetWsChannelWithoutOrderType(str)
@@ -872,7 +819,6 @@ func TestGetWsChannelWithoutOrderType(t *testing.T) {
// TestOrderBookUpdateChecksumCalculator logic test
func TestOrderBookUpdateChecksumCalculator(t *testing.T) {
TestSetDefaults(t)
if !websocketEnabled {
t.Skip("Websocket not enabled, skipping")
}
@@ -902,7 +848,6 @@ func TestOrderBookUpdateChecksumCalculator(t *testing.T) {
// TestOrderBookUpdateChecksumCalculatorWithDash logic test
func TestOrderBookUpdateChecksumCalculatorWith8DecimalPlaces(t *testing.T) {
TestSetDefaults(t)
if !websocketEnabled {
t.Skip("Websocket not enabled, skipping")
}
@@ -964,7 +909,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
o.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -976,7 +921,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
TestSetDefaults(t)
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := o.GetFee(feeBuilder); resp != float64(0.0015) || err != nil {
@@ -1031,7 +975,6 @@ func TestGetFee(t *testing.T) {
// TestFormatWithdrawPermissions helper test
func TestFormatWithdrawPermissions(t *testing.T) {
TestSetDefaults(t)
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := o.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
@@ -1149,3 +1092,25 @@ func TestWithdrawInternationalBank(t *testing.T) {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
}
}
// TestGetOrderbook logic test
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "BTC-USDT"},
asset.Spot)
if err != nil {
t.Error(err)
}
_, err = o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "Payload"},
asset.Futures)
if err == nil {
t.Error("error cannot be nil")
}
_, err = o.GetOrderBook(okgroup.GetOrderBookRequest{InstrumentID: "BTC-USD-SWAP"},
asset.PerpetualSwap)
if err == nil {
t.Error("error cannot be nil")
}
}

File diff suppressed because one or more lines are too long

View File

@@ -233,8 +233,8 @@ func (o *OKGroup) PlaceMultipleSpotOrders(request []PlaceOrderRequest) (map[stri
var orderErrors []error
for currency, orderResponse := range resp {
for _, order := range orderResponse {
if !order.Result {
for i := range orderResponse {
if !orderResponse[i].Result {
orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
}
}
@@ -262,15 +262,15 @@ func (o *OKGroup) CancelMultipleSpotOrders(request CancelMultipleSpotOrdersReque
}
for currency, orderResponse := range resp {
for _, order := range orderResponse {
for i := range orderResponse {
cancellationResponse := CancelMultipleSpotOrdersResponse{
OrderID: order.OrderID,
Result: order.Result,
ClientOID: order.ClientOID,
OrderID: orderResponse[i].OrderID,
Result: orderResponse[i].Result,
ClientOID: orderResponse[i].ClientOID,
}
if !order.Result {
cancellationResponse.Error = fmt.Errorf("order %v for currency %v failed to be cancelled", order.OrderID, currency)
if !orderResponse[i].Result {
cancellationResponse.Error = fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency)
}
resp[currency] = append(resp[currency], cancellationResponse)
@@ -454,8 +454,8 @@ func (o *OKGroup) PlaceMultipleMarginOrders(request []PlaceOrderRequest) (map[st
var orderErrors []error
for currency, orderResponse := range resp {
for _, order := range orderResponse {
if !order.Result {
for i := range orderResponse {
if !orderResponse[i].Result {
orderErrors = append(orderErrors, fmt.Errorf("order for currency %v failed to be placed", currency))
}
}
@@ -484,9 +484,9 @@ func (o *OKGroup) CancelMultipleMarginOrders(request CancelMultipleSpotOrdersReq
var orderErrors []error
for currency, orderResponse := range resp {
for _, order := range orderResponse {
if !order.Result {
orderErrors = append(orderErrors, fmt.Errorf("order %v for currency %v failed to be cancelled", order.OrderID, currency))
for i := range orderResponse {
if !orderResponse[i].Result {
orderErrors = append(orderErrors, fmt.Errorf("order %v for currency %v failed to be cancelled", orderResponse[i].OrderID, currency))
}
}
}

View File

@@ -1,78 +0,0 @@
package okgroup
import (
"log"
"os"
"testing"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
)
const (
apiKey = ""
apiSecret = ""
testAPIURL = "https://www.okex.com/api/"
testAPIVersion = "/v3/"
)
var o OKGroup
func TestMain(m *testing.M) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("okgroup load config error", err)
}
okgroup, err := cfg.GetExchangeConfig("Okex")
if err != nil {
log.Fatal("okgroup Setup() init error", err)
}
okgroup.API.AuthenticatedSupport = true
okgroup.API.Credentials.Key = apiKey
okgroup.API.Credentials.Secret = apiSecret
o.API.Endpoints.URL = testAPIURL
o.APIVersion = testAPIVersion
o.Requester = request.New("okgroup_test_things",
request.NewRateLimit(time.Second, 10),
request.NewRateLimit(time.Second, 10),
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
)
o.Websocket = wshandler.New()
err = o.Setup(okgroup)
if err != nil {
log.Fatal("okgroup setup error", err)
}
os.Exit(m.Run())
}
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := o.GetOrderBook(GetOrderBookRequest{InstrumentID: "BTC-USDT"},
asset.Spot)
if err != nil {
t.Error(err)
}
// futures expire and break test, will need to mock this in the future
_, err = o.GetOrderBook(GetOrderBookRequest{InstrumentID: "Payload"},
asset.Futures)
if err == nil {
t.Error("error cannot be nil")
}
_, err = o.GetOrderBook(GetOrderBookRequest{InstrumentID: "BTC-USD-SWAP"},
asset.PerpetualSwap)
if err != nil {
t.Error(err)
}
}

View File

@@ -273,6 +273,9 @@ func (o *OKGroup) SubmitOrder(s *order.Submit) (resp order.SubmitResponse, err e
resp.IsOrderPlaced = orderResponse.Result
resp.OrderID = orderResponse.OrderID
if s.OrderType == order.Market {
resp.FullyMatched = true
}
return
}

View File

@@ -1,6 +1,7 @@
package order
import (
"errors"
"strings"
"testing"
"time"
@@ -398,3 +399,138 @@ func TestSortOrdersByOrderType(t *testing.T) {
orders[0].OrderType)
}
}
var stringsToOrderSide = []struct {
in string
out Side
err error
}{
{"buy", Buy, nil},
{"BUY", Buy, nil},
{"bUy", Buy, nil},
{"sell", Sell, nil},
{"SELL", Sell, nil},
{"sElL", Sell, nil},
{"bid", Bid, nil},
{"BID", Bid, nil},
{"bId", Bid, nil},
{"ask", Ask, nil},
{"ASK", Ask, nil},
{"aSk", Ask, nil},
{"any", AnySide, nil},
{"ANY", AnySide, nil},
{"aNy", AnySide, nil},
{"woahMan", Buy, errors.New("woahMan not recognised as side type")},
}
func TestStringToOrderSide(t *testing.T) {
for i := 0; i < len(stringsToOrderSide); i++ {
testData := &stringsToOrderSide[i]
t.Run(testData.in, func(t *testing.T) {
out, err := StringToOrderSide(testData.in)
if err != nil {
if err.Error() != testData.err.Error() {
t.Error("Unexpected error", err)
}
} else if out != testData.out {
t.Errorf("Unexpected output %v. Expected %v", out, testData.out)
}
})
}
}
var stringsToOrderType = []struct {
in string
out Type
err error
}{
{"limit", Limit, nil},
{"LIMIT", Limit, nil},
{"lImIt", Limit, nil},
{"market", Market, nil},
{"MARKET", Market, nil},
{"mArKeT", Market, nil},
{"immediate_or_cancel", ImmediateOrCancel, nil},
{"IMMEDIATE_OR_CANCEL", ImmediateOrCancel, nil},
{"iMmEdIaTe_Or_CaNcEl", ImmediateOrCancel, nil},
{"stop", Stop, nil},
{"STOP", Stop, nil},
{"sToP", Stop, nil},
{"trailingstop", TrailingStop, nil},
{"TRAILINGSTOP", TrailingStop, nil},
{"tRaIlInGsToP", TrailingStop, nil},
{"any", AnyType, nil},
{"ANY", AnyType, nil},
{"aNy", AnyType, nil},
{"woahMan", Unknown, errors.New("woahMan not recognised as order type")},
}
func TestStringToOrderType(t *testing.T) {
for i := 0; i < len(stringsToOrderType); i++ {
testData := &stringsToOrderType[i]
t.Run(testData.in, func(t *testing.T) {
out, err := StringToOrderType(testData.in)
if err != nil {
if err.Error() != testData.err.Error() {
t.Error("Unexpected error", err)
}
} else if out != testData.out {
t.Errorf("Unexpected output %v. Expected %v", out, testData.out)
}
})
}
}
var stringsToOrderStatus = []struct {
in string
out Status
err error
}{
{"any", AnyStatus, nil},
{"ANY", AnyStatus, nil},
{"aNy", AnyStatus, nil},
{"new", New, nil},
{"NEW", New, nil},
{"nEw", New, nil},
{"active", Active, nil},
{"ACTIVE", Active, nil},
{"aCtIvE", Active, nil},
{"partially_filled", PartiallyFilled, nil},
{"PARTIALLY_FILLED", PartiallyFilled, nil},
{"pArTiAlLy_FiLlEd", PartiallyFilled, nil},
{"filled", Filled, nil},
{"FILLED", Filled, nil},
{"fIlLeD", Filled, nil},
{"canceled", Cancelled, nil},
{"CANCELED", Cancelled, nil},
{"cAnCelEd", Cancelled, nil},
{"pending_cancel", PendingCancel, nil},
{"PENDING_CANCEL", PendingCancel, nil},
{"pENdInG_cAnCeL", PendingCancel, nil},
{"rejected", Rejected, nil},
{"REJECTED", Rejected, nil},
{"rEjEcTeD", Rejected, nil},
{"expired", Expired, nil},
{"EXPIRED", Expired, nil},
{"eXpIrEd", Expired, nil},
{"hidden", Hidden, nil},
{"HIDDEN", Hidden, nil},
{"hIdDeN", Hidden, nil},
{"woahMan", UnknownStatus, errors.New("woahMan not recognised as order STATUS")},
}
func TestStringToOrderStatus(t *testing.T) {
for i := 0; i < len(stringsToOrderStatus); i++ {
testData := &stringsToOrderStatus[i]
t.Run(testData.in, func(t *testing.T) {
out, err := StringToOrderStatus(testData.in)
if err != nil {
if err.Error() != testData.err.Error() {
t.Error("Unexpected error", err)
}
} else if out != testData.out {
t.Errorf("Unexpected output %v. Expected %v", out, testData.out)
}
})
}
}

View File

@@ -50,6 +50,7 @@ type Submit struct {
// SubmitResponse is what is returned after submitting an order to an exchange
type SubmitResponse struct {
IsOrderPlaced bool
FullyMatched bool
OrderID string
}
@@ -127,7 +128,7 @@ type Detail struct {
// TradeHistory holds exchange history data
type TradeHistory struct {
Timestamp time.Time
TID int64
TID string
Price float64
Amount float64
Exchange string

View File

@@ -1,6 +1,7 @@
package order
import (
"fmt"
"sort"
"strings"
"time"
@@ -10,18 +11,18 @@ import (
// NewOrder creates a new order and returns a an orderID
func NewOrder(exchangeName string, amount, price float64) int {
order := &Order{}
ord := &Order{}
if len(Orders) == 0 {
order.OrderID = 0
ord.OrderID = 0
} else {
order.OrderID = len(Orders)
ord.OrderID = len(Orders)
}
order.Exchange = exchangeName
order.Amount = amount
order.Price = price
Orders = append(Orders, order)
return order.OrderID
ord.Exchange = exchangeName
ord.Amount = amount
ord.Price = price
Orders = append(Orders, ord)
return ord.OrderID
}
// DeleteOrder deletes orders by ID and returns state
@@ -300,3 +301,72 @@ func SortOrdersBySide(orders *[]Detail, reverse bool) {
sort.Sort(ByOrderSide(*orders))
}
}
// StringToOrderSide for converting case insensitive order side
// and returning a real Side
func StringToOrderSide(side string) (Side, error) {
switch {
case strings.EqualFold(side, Buy.String()):
return Buy, nil
case strings.EqualFold(side, Sell.String()):
return Sell, nil
case strings.EqualFold(side, Bid.String()):
return Bid, nil
case strings.EqualFold(side, Ask.String()):
return Ask, nil
case strings.EqualFold(side, AnySide.String()):
return AnySide, nil
default:
return Side(""), fmt.Errorf("%s not recognised as side type", side)
}
}
// StringToOrderType for converting case insensitive order type
// and returning a real Type
func StringToOrderType(oType string) (Type, error) {
switch {
case strings.EqualFold(oType, Limit.String()):
return Limit, nil
case strings.EqualFold(oType, Market.String()):
return Market, nil
case strings.EqualFold(oType, ImmediateOrCancel.String()):
return ImmediateOrCancel, nil
case strings.EqualFold(oType, Stop.String()):
return Stop, nil
case strings.EqualFold(oType, TrailingStop.String()):
return TrailingStop, nil
case strings.EqualFold(oType, AnyType.String()):
return AnyType, nil
default:
return Unknown, fmt.Errorf("%s not recognised as order type", oType)
}
}
// StringToOrderStatus for converting case insensitive order status
// and returning a real Status
func StringToOrderStatus(status string) (Status, error) {
switch {
case strings.EqualFold(status, AnyStatus.String()):
return AnyStatus, nil
case strings.EqualFold(status, New.String()):
return New, nil
case strings.EqualFold(status, Active.String()):
return Active, nil
case strings.EqualFold(status, PartiallyFilled.String()):
return PartiallyFilled, nil
case strings.EqualFold(status, Filled.String()):
return Filled, nil
case strings.EqualFold(status, Cancelled.String()):
return Cancelled, nil
case strings.EqualFold(status, PendingCancel.String()):
return PendingCancel, nil
case strings.EqualFold(status, Rejected.String()):
return Rejected, nil
case strings.EqualFold(status, Expired.String()):
return Expired, nil
case strings.EqualFold(status, Hidden.String()):
return Hidden, nil
default:
return UnknownStatus, fmt.Errorf("%s not recognised as order STATUS", status)
}
}

View File

@@ -158,9 +158,9 @@ func TestVerify(t *testing.T) {
func TestCalculateTotalBids(t *testing.T) {
t.Parallel()
currency := currency.NewPairFromStrings("BTC", "USD")
curr := currency.NewPairFromStrings("BTC", "USD")
base := Base{
Pair: currency,
Pair: curr,
Bids: []Item{{Price: 100, Amount: 10}},
LastUpdated: time.Now(),
}
@@ -173,9 +173,9 @@ func TestCalculateTotalBids(t *testing.T) {
func TestCalculateTotaAsks(t *testing.T) {
t.Parallel()
currency := currency.NewPairFromStrings("BTC", "USD")
curr := currency.NewPairFromStrings("BTC", "USD")
base := Base{
Pair: currency,
Pair: curr,
Asks: []Item{{Price: 100, Amount: 10}},
}
@@ -187,10 +187,10 @@ func TestCalculateTotaAsks(t *testing.T) {
func TestUpdate(t *testing.T) {
t.Parallel()
currency := currency.NewPairFromStrings("BTC", "USD")
curr := currency.NewPairFromStrings("BTC", "USD")
timeNow := time.Now()
base := Base{
Pair: currency,
Pair: curr,
Asks: []Item{{Price: 100, Amount: 10}},
Bids: []Item{{Price: 200, Amount: 10}},
LastUpdated: timeNow,

View File

@@ -103,7 +103,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
p.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
@@ -202,9 +202,7 @@ func TestFormatWithdrawPermissions(t *testing.T) {
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText +
" & " +
exchange.NoFiatWithdrawalsText
withdrawPermissions := p.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s",
expectedResult,
@@ -284,7 +282,6 @@ func TestCancelExchangeOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -310,7 +307,6 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",

View File

@@ -469,12 +469,12 @@ func (p *Poloniex) WsProcessOrderbookUpdate(sequenceNumber int64, target []inter
func (p *Poloniex) GenerateDefaultSubscriptions() {
var subscriptions []wshandler.WebsocketChannelSubscription
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
Channel: fmt.Sprintf("%v", wsTickerDataID),
Channel: strconv.FormatInt(wsTickerDataID, 10),
})
if p.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
Channel: fmt.Sprintf("%v", wsAccountNotificationID),
Channel: strconv.FormatInt(wsAccountNotificationID, 10),
})
}
@@ -495,9 +495,9 @@ func (p *Poloniex) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscr
Command: "subscribe",
}
switch {
case strings.EqualFold(fmt.Sprintf("%v", wsAccountNotificationID), channelToSubscribe.Channel):
case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), channelToSubscribe.Channel):
return p.wsSendAuthorisedCommand("subscribe")
case strings.EqualFold(fmt.Sprintf("%v", wsTickerDataID), channelToSubscribe.Channel):
case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), channelToSubscribe.Channel):
subscriptionRequest.Channel = wsTickerDataID
default:
subscriptionRequest.Channel = channelToSubscribe.Currency.String()
@@ -511,9 +511,9 @@ func (p *Poloniex) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubs
Command: "unsubscribe",
}
switch {
case strings.EqualFold(fmt.Sprintf("%v", wsAccountNotificationID), channelToSubscribe.Channel):
case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), channelToSubscribe.Channel):
return p.wsSendAuthorisedCommand("unsubscribe")
case strings.EqualFold(fmt.Sprintf("%v", wsTickerDataID), channelToSubscribe.Channel):
case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), channelToSubscribe.Channel):
unsubscriptionRequest.Channel = wsTickerDataID
default:
unsubscriptionRequest.Channel = channelToSubscribe.Currency.String()

View File

@@ -238,13 +238,14 @@ func (p *Poloniex) UpdateTicker(currencyPair currency.Pair, assetType asset.Item
return tickerPrice, err
}
for _, x := range p.GetEnabledPairs(assetType) {
enabledPairs := p.GetEnabledPairs(assetType)
for i := range enabledPairs {
var tp ticker.Price
curr := p.FormatExchangeCurrency(x, assetType).String()
curr := p.FormatExchangeCurrency(enabledPairs[i], assetType).String()
if _, ok := tick[curr]; !ok {
continue
}
tp.Pair = x
tp.Pair = enabledPairs[i]
tp.Ask = tick[curr].LowestAsk
tp.Bid = tick[curr].HighestBid
tp.High = tick[curr].High24Hr
@@ -287,9 +288,9 @@ func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.I
return orderBook, err
}
for _, x := range p.GetEnabledPairs(assetType) {
currency := p.FormatExchangeCurrency(x, assetType).String()
data, ok := orderbookNew.Data[currency]
enabledPairs := p.GetEnabledPairs(assetType)
for i := range enabledPairs {
data, ok := orderbookNew.Data[p.FormatExchangeCurrency(enabledPairs[i], assetType).String()]
if !ok {
continue
}
@@ -307,7 +308,7 @@ func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.I
Amount: data.Asks[y].Amount, Price: data.Asks[y].Price})
}
orderBook.Asks = obItems
orderBook.Pair = x
orderBook.Pair = enabledPairs[i]
orderBook.ExchangeName = p.Name
orderBook.AssetType = assetType
@@ -370,13 +371,18 @@ func (p *Poloniex) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
false,
fillOrKill,
isBuyOrder)
if err != nil {
return submitOrderResponse, err
}
if response.OrderNumber > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response.OrderNumber, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
submitOrderResponse.IsOrderPlaced = true
if s.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -327,6 +327,17 @@ func (w *Websocket) IsConnectionMonitorRunning() bool {
return w.connectionMonitorRunning
}
// CanUseAuthenticatedWebsocketForWrapper Handles a common check to
// verify whether a wrapper can use an authenticated websocket endpoint
func (w *Websocket) CanUseAuthenticatedWebsocketForWrapper() bool {
if w.IsConnected() && w.CanUseAuthenticatedEndpoints() {
return true
} else if w.IsConnected() && !w.CanUseAuthenticatedEndpoints() {
log.Infof(log.WebsocketMgr, WebsocketNotAuthenticatedUsingRest, w.exchangeName)
}
return false
}
// SetWebsocketURL sets websocket URL
func (w *Websocket) SetWebsocketURL(websocketURL string) {
if websocketURL == "" || websocketURL == config.WebsocketURLNonDefaultMessage {
@@ -629,7 +640,6 @@ func (w *WebsocketConnection) Dial(dialer *websocket.Dialer, headers http.Header
}
dialer.Proxy = http.ProxyURL(proxy)
}
var err error
var conStatus *http.Response
w.Connection, conStatus, err = dialer.Dial(w.URL, headers)
@@ -640,7 +650,7 @@ func (w *WebsocketConnection) Dial(dialer *websocket.Dialer, headers http.Header
return fmt.Errorf("%v Error: %v", w.URL, err)
}
if w.Verbose {
log.Infof(log.WebsocketMgr, "%v Websocket connected", w.ExchangeName)
log.Infof(log.WebsocketMgr, "%v Websocket connected to %s", w.ExchangeName, w.URL)
}
w.setConnectedStatus(true)
return nil
@@ -703,6 +713,12 @@ func (w *WebsocketConnection) WaitForResult(id int64, wg *sync.WaitGroup) {
for k := range w.IDResponses {
if k == id {
w.Unlock()
if !timer.Stop() {
select {
case <-timer.C:
default:
}
}
return
}
}

View File

@@ -678,3 +678,22 @@ func readMessages(wc *WebsocketConnection, t *testing.T) {
}
}
}
// TestCanUseAuthenticatedWebsocketForWrapper logic test
func TestCanUseAuthenticatedWebsocketForWrapper(t *testing.T) {
ws := &Websocket{}
resp := ws.CanUseAuthenticatedWebsocketForWrapper()
if resp {
t.Error("Expected false, `connected` is false")
}
ws.setConnectedStatus(true)
resp = ws.CanUseAuthenticatedWebsocketForWrapper()
if resp {
t.Error("Expected false, `connected` is true and `CanUseAuthenticatedEndpoints` is false")
}
ws.canUseAuthenticatedEndpoints = true
resp = ws.CanUseAuthenticatedWebsocketForWrapper()
if !resp {
t.Error("Expected true, `connected` and `CanUseAuthenticatedEndpoints` is true")
}
}

View File

@@ -17,7 +17,8 @@ const (
WebsocketNotEnabled = "exchange_websocket_not_enabled"
manageSubscriptionsDelay = 5 * time.Second
// connection monitor time delays and limits
connectionMonitorDelay = 2 * time.Second
connectionMonitorDelay = 2 * time.Second
WebsocketNotAuthenticatedUsingRest = "%v - Websocket not authenticated, using REST"
)
// Websocket defines a return type for websocket connections via the interface

View File

@@ -1,7 +1,9 @@
package yobit
import (
"log"
"math"
"os"
"testing"
"time"
@@ -22,19 +24,16 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
y.SetDefaults()
}
func TestSetup(t *testing.T) {
yobitConfig := config.GetConfig()
err := yobitConfig.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("Yobit load config error", err)
log.Fatal("Yobit load config error", err)
}
conf, err := yobitConfig.GetExchangeConfig("Yobit")
if err != nil {
t.Fatal("Yobit init error", err)
log.Fatal("Yobit init error", err)
}
conf.API.Credentials.Key = apiKey
conf.API.Credentials.Secret = apiSecret
@@ -42,8 +41,9 @@ func TestSetup(t *testing.T) {
err = y.Setup(conf)
if err != nil {
t.Fatal("Yobit setup error", err)
log.Fatal("Yobit setup error", err)
}
os.Exit(m.Run())
}
func TestFetchTradablePairs(t *testing.T) {
@@ -167,7 +167,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
y.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -179,8 +179,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
y.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
@@ -317,20 +315,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
y.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := y.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
y.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC,
@@ -346,9 +338,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
y.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC,
@@ -372,9 +361,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
y.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -400,15 +386,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
y.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -426,15 +408,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
y.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -457,6 +435,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := y.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -464,8 +445,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
y.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -489,9 +468,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
y.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -506,9 +482,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
y.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -523,7 +496,7 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() {
_, err := y.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("GetDepositAddress() Expected error")

View File

@@ -187,20 +187,22 @@ func (y *Yobit) UpdateTicker(p currency.Pair, assetType asset.Item) (ticker.Pric
return tickerPrice, err
}
for _, x := range y.GetEnabledPairs(assetType) {
curr := y.FormatExchangeCurrency(x, assetType).Lower().String()
enabledPairs := y.GetEnabledPairs(assetType)
for i := range enabledPairs {
curr := y.FormatExchangeCurrency(enabledPairs[i], assetType).Lower().String()
if _, ok := result[curr]; !ok {
continue
}
resultCurr := result[curr]
var tickerPrice ticker.Price
tickerPrice.Pair = x
tickerPrice.Last = result[curr].Last
tickerPrice.Ask = result[curr].Sell
tickerPrice.Bid = result[curr].Buy
tickerPrice.Last = result[curr].Last
tickerPrice.Low = result[curr].Low
tickerPrice.QuoteVolume = result[curr].VolumeCurrent
tickerPrice.Volume = result[curr].Vol
tickerPrice.Pair = enabledPairs[i]
tickerPrice.Last = resultCurr.Last
tickerPrice.Ask = resultCurr.Sell
tickerPrice.Bid = resultCurr.Buy
tickerPrice.Last = resultCurr.Last
tickerPrice.Low = resultCurr.Low
tickerPrice.QuoteVolume = resultCurr.VolumeCurrent
tickerPrice.Volume = resultCurr.Vol
err = ticker.ProcessTicker(y.Name, &tickerPrice, assetType)
if err != nil {
@@ -323,13 +325,15 @@ func (y *Yobit) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
s.OrderSide.String(),
s.Amount,
s.Price)
if err != nil {
return submitOrderResponse, err
}
if response > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response, 10)
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
submitOrderResponse.IsOrderPlaced = true
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to

View File

@@ -423,10 +423,10 @@ func (z *ZB) Withdraw(currency, address, safepassword string, amount, fees float
vals := url.Values{}
vals.Set("accesskey", z.API.Credentials.Key)
vals.Set("amount", fmt.Sprintf("%v", amount))
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
vals.Set("currency", currency)
vals.Set("fees", fmt.Sprintf("%v", fees))
vals.Set("itransfer", fmt.Sprintf("%v", itransfer))
vals.Set("fees", strconv.FormatFloat(fees, 'f', -1, 64))
vals.Set("itransfer", strconv.FormatBool(itransfer))
vals.Set("method", "withdraw")
vals.Set("recieveAddr", address)
vals.Set("safePwd", safepassword)

View File

@@ -3,7 +3,10 @@ package zb
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"testing"
"github.com/gorilla/websocket"
@@ -25,19 +28,16 @@ const (
var z ZB
var wsSetupRan bool
func TestSetDefaults(t *testing.T) {
func TestMain(m *testing.M) {
z.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
t.Fatal("ZB load config error", err)
log.Fatal("ZB load config error", err)
}
zbConfig, err := cfg.GetExchangeConfig("ZB")
if err != nil {
t.Fatal("ZB Setup() init error", err)
log.Fatal("ZB Setup() init error", err)
}
zbConfig.API.AuthenticatedSupport = true
zbConfig.API.AuthenticatedWebsocketSupport = true
@@ -46,16 +46,16 @@ func TestSetup(t *testing.T) {
err = z.Setup(zbConfig)
if err != nil {
t.Fatal("ZB setup error", err)
log.Fatal("ZB setup error", err)
}
os.Exit(m.Run())
}
func setupWsAuth(t *testing.T) {
if wsSetupRan {
return
}
z.SetDefaults()
TestSetup(t)
if !z.Websocket.IsEnabled() && !z.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip(wshandler.WebsocketNotEnabled)
}
@@ -90,11 +90,9 @@ func TestSpotNewOrder(t *testing.T) {
Amount: 0.01,
Price: 10246.1,
}
orderid, err := z.SpotNewOrder(arg)
_, err := z.SpotNewOrder(arg)
if err != nil {
t.Errorf("ZB SpotNewOrder: %s", err)
} else {
t.Log(orderid)
}
}
@@ -182,7 +180,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
var feeBuilder = setFeeBuilder()
z.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -194,8 +192,6 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
z.SetDefaults()
TestSetup(t)
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
@@ -272,20 +268,14 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
z.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := z.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
z.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
Currencies: []currency.Pair{currency.NewPair(currency.XRP,
@@ -301,9 +291,6 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
z.SetDefaults()
TestSetup(t)
var getOrdersRequest = order.GetOrdersRequest{
OrderType: order.AnyType,
OrderSide: order.Buy,
@@ -326,9 +313,6 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
z.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip(fmt.Sprintf("ApiKey: %s. Can place orders: %v",
z.API.Credentials.Key,
@@ -356,15 +340,11 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
z.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.XRP, currency.USDT)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -382,15 +362,11 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
z.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.XRP, currency.USDT)
var orderCancellation = &order.Cancel{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
@@ -427,6 +403,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := z.ModifyOrder(&order.Modify{})
if err == nil {
t.Error("ModifyOrder() Expected error")
@@ -434,8 +413,6 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
z.SetDefaults()
TestSetup(t)
withdrawCryptoRequest := exchange.CryptoWithdrawRequest{
GenericWithdrawRequestInfo: exchange.GenericWithdrawRequestInfo{
Amount: -1,
@@ -460,9 +437,6 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
z.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -475,9 +449,6 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
z.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -490,7 +461,7 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() {
_, err := z.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("GetDepositAddress() error PLEASE MAKE SURE YOU CREATE DEPOSIT ADDRESSES VIA ZB.COM",
@@ -548,7 +519,7 @@ func TestWsCreateSuUserKey(t *testing.T) {
t.Fatal(err)
}
userID := subUsers.Message[0].UserID
_, err = z.wsCreateSubUserKey(true, true, true, true, "subu", fmt.Sprintf("%v", userID))
_, err = z.wsCreateSubUserKey(true, true, true, true, "subu", strconv.FormatInt(userID, 10))
if err != nil {
t.Fatal(err)
}

View File

@@ -16,7 +16,7 @@ type OrderbookResponse struct {
// AccountsResponseCoin holds the accounts coin details
type AccountsResponseCoin struct {
Freez string `json:"freez"` // 冻结资产
Freeze string `json:"freez"` // 冻结资产
EnName string `json:"enName"` // 币种英文名
UnitDecimal int `json:"unitDecimal"` // 保留小数位
UnName string `json:"cnName"` // 币种中文名
@@ -44,6 +44,9 @@ type Order struct {
TradeDate int `json:"trade_date"`
TradeMoney float64 `json:"trade_money"`
Type int64 `json:"type"`
Fees float64 `json:"fees,omitempty"`
TradePrice float64 `json:"trade_price,omitempty"`
No int64 `json:"no,string,omitempty"`
}
// AccountsResponse 用户基本信息

View File

@@ -187,40 +187,6 @@ func (z *ZB) WsHandleData() {
}
}
var wsErrCodes = map[int64]string{
1000: "Successful call",
1001: "General error message",
1002: "internal error",
1003: "Verification failed",
1004: "Financial security password lock",
1005: "The fund security password is incorrect. Please confirm and re-enter.",
1006: "Real-name certification is awaiting review or review",
1007: "Channel is empty",
1008: "Event is empty",
1009: "This interface is being maintained",
1011: "Not open yet",
1012: "Insufficient permissions",
1013: "Can not trade, if you have any questions, please contact online customer service",
1014: "Cannot be sold during the pre-sale period",
2002: "Insufficient balance in Bitcoin account",
2003: "Insufficient balance of Litecoin account",
2005: "Insufficient balance in Ethereum account",
2006: "Insufficient balance in ETC currency account",
2007: "Insufficient balance of BTS currency account",
2008: "Insufficient balance in EOS currency account",
2009: "Insufficient account balance",
3001: "Pending order not found",
3002: "Invalid amount",
3003: "Invalid quantity",
3004: "User does not exist",
3005: "Invalid parameter",
3006: "Invalid IP or inconsistent with the bound IP",
3007: "Request time has expired",
3008: "Transaction history not found",
4001: "API interface is locked",
4002: "Request too frequently",
}
// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions()
func (z *ZB) GenerateDefaultSubscriptions() {
var subscriptions []wshandler.WebsocketChannelSubscription

View File

@@ -191,28 +191,12 @@ type WsGetOrderRequest struct {
// WsGetOrderResponse contains order data
type WsGetOrderResponse struct {
Message string `json:"message"`
No int64 `json:"no,string"`
Code int64 `json:"code"`
Channel string `json:"channel"`
Success bool `json:"success"`
Data WsGetOrderResponseData `json:"data"`
}
// WsGetOrderResponseData Detailed order data
type WsGetOrderResponseData struct {
Currency string `json:"currency"`
Fees float64 `json:"fees"`
ID string `json:"id"`
Price float64 `json:"price"`
Status int64 `json:"status"`
TotalAmount float64 `json:"total_amount"`
TradeAmount float64 `json:"trade_amount"`
TradePrice float64 `json:"trade_price"`
TradeDate int64 `json:"trade_date"`
TradeMoney float64 `json:"trade_money"`
Type int64 `json:"type"`
No int64 `json:"no,string"`
Message string `json:"message"`
No int64 `json:"no,string"`
Code int64 `json:"code"`
Channel string `json:"channel"`
Success bool `json:"success"`
Data []Order `json:"data"`
}
// WsGetOrdersRequest get more orders, with no orderID filtering
@@ -228,12 +212,12 @@ type WsGetOrdersRequest struct {
// WsGetOrdersResponse contains orders data
type WsGetOrdersResponse struct {
Message string `json:"message"`
No int64 `json:"no,string"`
Code int64 `json:"code"`
Channel string `json:"channel"`
Success bool `json:"success"`
Data []WsGetOrderResponseData `json:"data"`
Message string `json:"message"`
No int64 `json:"no,string"`
Code int64 `json:"code"`
Channel string `json:"channel"`
Success bool `json:"success"`
Data []Order `json:"data"`
}
// WsGetOrdersIgnoreTradeTypeRequest ws request
@@ -249,12 +233,12 @@ type WsGetOrdersIgnoreTradeTypeRequest struct {
// WsGetOrdersIgnoreTradeTypeResponse contains orders data
type WsGetOrdersIgnoreTradeTypeResponse struct {
Message string `json:"message"`
No int64 `json:"no,string"`
Code int64 `json:"code"`
Channel string `json:"channel"`
Success bool `json:"success"`
Data []WsGetOrderResponseData `json:"data"`
Message string `json:"message"`
No int64 `json:"no,string"`
Code int64 `json:"code"`
Channel string `json:"channel"`
Success bool `json:"success"`
Data []Order `json:"data"`
}
// WsGetAccountInfoResponse contains account data
@@ -262,23 +246,44 @@ type WsGetAccountInfoResponse struct {
Message string `json:"message"`
No int64 `json:"no,string"`
Data struct {
Coins []struct {
Freez float64 `json:"freez,string"`
EnName string `json:"enName"`
UnitDecimal int64 `json:"unitDecimal"`
CnName string `json:"cnName"`
UnitTag string `json:"unitTag"`
Available float64 `json:"available,string"`
Key string `json:"key"`
} `json:"coins"`
Base struct {
Username string `json:"username"`
TradePasswordEnabled bool `json:"trade_password_enabled"`
AuthGoogleEnabled bool `json:"auth_google_enabled"`
AuthMobileEnabled bool `json:"auth_mobile_enabled"`
} `json:"base"`
Coins []AccountsResponseCoin `json:"coins"`
Base AccountsBaseResponse `json:"base"`
} `json:"data"`
Code int64 `json:"code"`
Channel string `json:"channel"`
Success bool `json:"success"`
}
var wsErrCodes = map[int64]string{
1000: "Successful call",
1001: "General error message",
1002: "internal error",
1003: "Verification failed",
1004: "Financial security password lock",
1005: "The fund security password is incorrect. Please confirm and re-enter.",
1006: "Real-name certification is awaiting review or review",
1007: "Channel is empty",
1008: "Event is empty",
1009: "This interface is being maintained",
1011: "Not open yet",
1012: "Insufficient permissions",
1013: "Can not trade, if you have any questions, please contact online customer service",
1014: "Cannot be sold during the pre-sale period",
2002: "Insufficient balance in Bitcoin account",
2003: "Insufficient balance of Litecoin account",
2005: "Insufficient balance in Ethereum account",
2006: "Insufficient balance in ETC currency account",
2007: "Insufficient balance of BTS currency account",
2008: "Insufficient balance in EOS currency account",
2009: "Insufficient account balance",
3001: "Pending order not found",
3002: "Invalid amount",
3003: "Invalid quantity",
3004: "User does not exist",
3005: "Invalid parameter",
3006: "Invalid IP or inconsistent with the bound IP",
3007: "Request time has expired",
3008: "Transaction history not found",
4001: "API interface is locked",
4002: "Request too frequently",
}

View File

@@ -2,6 +2,7 @@ package zb
import (
"errors"
"fmt"
"strconv"
"strings"
"sync"
@@ -268,8 +269,9 @@ func (z *ZB) FetchOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Ba
// UpdateOrderbook updates and returns the orderbook for a currency pair
func (z *ZB) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.Base, error) {
var orderBook orderbook.Base
orderbookNew, err := z.GetOrderbook(z.FormatExchangeCurrency(p,
assetType).String())
curr := z.FormatExchangeCurrency(p, assetType).String()
orderbookNew, err := z.GetOrderbook(curr)
if err != nil {
return orderBook, err
}
@@ -304,25 +306,35 @@ func (z *ZB) UpdateOrderbook(p currency.Pair, assetType asset.Item) (orderbook.B
// ZB exchange
func (z *ZB) GetAccountInfo() (exchange.AccountInfo, error) {
var info exchange.AccountInfo
bal, err := z.GetAccountInformation()
if err != nil {
return info, err
var balances []exchange.AccountCurrencyInfo
var coins []AccountsResponseCoin
if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
resp, err := z.wsGetAccountInfoRequest()
if err != nil {
return info, err
}
coins = resp.Data.Coins
} else {
bal, err := z.GetAccountInformation()
if err != nil {
return info, err
}
coins = bal.Result.Coins
}
var balances []exchange.AccountCurrencyInfo
for _, data := range bal.Result.Coins {
hold, err := strconv.ParseFloat(data.Freez, 64)
for i := range coins {
hold, err := strconv.ParseFloat(coins[i].Freeze, 64)
if err != nil {
return info, err
}
avail, err := strconv.ParseFloat(data.Available, 64)
avail, err := strconv.ParseFloat(coins[i].Available, 64)
if err != nil {
return info, err
}
balances = append(balances, exchange.AccountCurrencyInfo{
CurrencyName: currency.NewCode(data.EnName),
CurrencyName: currency.NewCode(coins[i].EnName),
TotalValue: hold + avail,
Hold: hold,
})
@@ -348,33 +360,53 @@ func (z *ZB) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchan
}
// SubmitOrder submits a new order
func (z *ZB) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
func (z *ZB) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) {
var submitOrderResponse order.SubmitResponse
if err := s.Validate(); err != nil {
err := o.Validate()
if err != nil {
return submitOrderResponse, err
}
var oT SpotNewOrderRequestParamsType
if s.OrderSide == order.Buy {
oT = SpotNewOrderRequestParamsTypeBuy
if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var isBuyOrder int64
if o.OrderSide == order.Buy {
isBuyOrder = 1
} else {
isBuyOrder = 0
}
var response *WsSubmitOrderResponse
response, err = z.wsSubmitOrder(o.Pair, o.Amount, o.Price, isBuyOrder)
if err != nil {
return submitOrderResponse, err
}
submitOrderResponse.OrderID = strconv.FormatInt(response.Data.EntrustID, 10)
} else {
oT = SpotNewOrderRequestParamsTypeSell
}
var oT SpotNewOrderRequestParamsType
if o.OrderSide == order.Buy {
oT = SpotNewOrderRequestParamsTypeBuy
} else {
oT = SpotNewOrderRequestParamsTypeSell
}
var params = SpotNewOrderRequestParams{
Amount: s.Amount,
Price: s.Price,
Symbol: s.Pair.Lower().String(),
Type: oT,
var params = SpotNewOrderRequestParams{
Amount: o.Amount,
Price: o.Price,
Symbol: o.Pair.Lower().String(),
Type: oT,
}
var response int64
response, err = z.SpotNewOrder(params)
if err != nil {
return submitOrderResponse, err
}
if response > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response, 10)
}
}
response, err := z.SpotNewOrder(params)
if response > 0 {
submitOrderResponse.OrderID = strconv.FormatInt(response, 10)
submitOrderResponse.IsOrderPlaced = true
if o.OrderType == order.Market {
submitOrderResponse.FullyMatched = true
}
if err == nil {
submitOrderResponse.IsOrderPlaced = true
}
return submitOrderResponse, err
return submitOrderResponse, nil
}
// ModifyOrder will allow of changing orderbook placement and limit to
@@ -389,8 +421,20 @@ func (z *ZB) CancelOrder(o *order.Cancel) error {
if err != nil {
return err
}
curr := z.FormatExchangeCurrency(o.CurrencyPair, o.AssetType).String()
return z.CancelExistingOrder(orderIDInt, curr)
if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var response *WsCancelOrderResponse
response, err = z.wsCancelOrder(o.CurrencyPair, orderIDInt)
if err != nil {
return err
}
if !response.Success {
return fmt.Errorf("%v - Could not cancel order %v", z.Name, o.OrderID)
}
return nil
}
return z.CancelExistingOrder(orderIDInt, z.FormatExchangeCurrency(o.CurrencyPair,
o.AssetType).String())
}
// CancelAllOrders cancels all orders associated with a currency pair
@@ -424,11 +468,12 @@ func (z *ZB) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) {
}
for i := range allOpenOrders {
err := z.CancelExistingOrder(allOpenOrders[i].ID,
allOpenOrders[i].Currency)
err := z.CancelOrder(&order.Cancel{
OrderID: strconv.FormatInt(allOpenOrders[i].ID, 10),
CurrencyPair: currency.NewPairFromString(allOpenOrders[i].Currency),
})
if err != nil {
ID := strconv.FormatInt(allOpenOrders[i].ID, 10)
cancelAllOrdersResponse.Status[ID] = err.Error()
cancelAllOrdersResponse.Status[strconv.FormatInt(allOpenOrders[i].ID, 10)] = err.Error()
}
}
@@ -539,35 +584,45 @@ func (z *ZB) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error
if req.OrderSide == order.AnySide || req.OrderSide == "" {
return nil, errors.New("specific order side is required")
}
var allOrders []Order
var orders []order.Detail
var side int64
if req.OrderSide == order.Buy {
side = 1
}
for x := range req.Currencies {
for y := int64(1); ; y++ {
fPair := z.FormatExchangeCurrency(req.Currencies[x], asset.Spot).String()
resp, err := z.GetOrders(fPair, y, side)
if err != nil {
return nil, err
if z.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
for x := range req.Currencies {
for y := int64(1); ; y++ {
resp, err := z.wsGetOrdersIgnoreTradeType(req.Currencies[x], y, 10)
if err != nil {
return nil, err
}
allOrders = append(allOrders, resp.Data...)
if len(resp.Data) != 10 {
break
}
}
if len(resp) == 0 {
break
}
allOrders = append(allOrders, resp...)
if len(resp) != 10 {
break
}
} else {
if req.OrderSide == order.Buy {
side = 1
}
for x := range req.Currencies {
for y := int64(1); ; y++ {
fPair := z.FormatExchangeCurrency(req.Currencies[x], asset.Spot).String()
resp, err := z.GetOrders(fPair, y, side)
if err != nil {
return nil, err
}
if len(resp) == 0 {
break
}
allOrders = append(allOrders, resp...)
if len(resp) != 10 {
break
}
}
}
}
var orders []order.Detail
for i := range allOrders {
symbol := currency.NewPairDelimiter(allOrders[i].Currency,
z.GetPairFormat(asset.Spot, false).Delimiter)