poloniex: websocket update (#659)

* poloniex: websocket update with debug output and currency tracking system

* linter: fix issues

* nits: Addr

* poloniex: govet fix

* nits: addr

* Bittrex: Fix fee test
This commit is contained in:
Ryan O'Hara-Reid
2021-04-27 12:05:10 +10:00
committed by GitHub
parent d106d091e6
commit ca87ddf825
12 changed files with 4123 additions and 435 deletions

View File

@@ -9,6 +9,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/stats" "github.com/thrasher-corp/gocryptotrader/exchanges/stats"
@@ -323,6 +324,11 @@ func (bot *Engine) WebsocketDataHandler(exchName string, data interface{}) error
} }
printOrderbookSummary(d, "websocket", bot, nil) printOrderbookSummary(d, "websocket", bot, nil)
case *order.Detail: case *order.Detail:
if bot.Settings.Verbose {
printOrderSummary(d)
}
// TODO: Dont check if exists this creates two locks, on conflict update
// else insert.
if !bot.OrderManager.orderStore.exists(d) { if !bot.OrderManager.orderStore.exists(d) {
err := bot.OrderManager.orderStore.Add(d) err := bot.OrderManager.orderStore.Add(d)
if err != nil { if err != nil {
@@ -335,9 +341,11 @@ func (bot *Engine) WebsocketDataHandler(exchName string, data interface{}) error
} }
od.UpdateOrderFromDetail(d) od.UpdateOrderFromDetail(d)
} }
case *order.Cancel:
return bot.OrderManager.Cancel(d)
case *order.Modify: case *order.Modify:
if bot.Settings.Verbose {
printOrderChangeSummary(d)
}
// TODO: On conflict update or insert if not found
od, err := bot.OrderManager.orderStore.GetByExchangeAndID(d.Exchange, d.ID) od, err := bot.OrderManager.orderStore.GetByExchangeAndID(d.Exchange, d.ID)
if err != nil { if err != nil {
return err return err
@@ -347,6 +355,10 @@ func (bot *Engine) WebsocketDataHandler(exchName string, data interface{}) error
return errors.New(d.Error()) return errors.New(d.Error())
case stream.UnhandledMessageWarning: case stream.UnhandledMessageWarning:
log.Warn(log.WebsocketMgr, d.Message) log.Warn(log.WebsocketMgr, d.Message)
case account.Change:
if bot.Settings.Verbose {
printAccountHoldingsChangeSummary(d)
}
default: default:
if bot.Settings.Verbose { if bot.Settings.Verbose {
log.Warnf(log.WebsocketMgr, log.Warnf(log.WebsocketMgr,
@@ -357,3 +369,59 @@ func (bot *Engine) WebsocketDataHandler(exchName string, data interface{}) error
} }
return nil return nil
} }
// printOrderChangeSummary this function will be deprecated when a order manager
// update is done.
func printOrderChangeSummary(m *order.Modify) {
if m == nil {
return
}
log.Debugf(log.WebsocketMgr,
"Order Change: %s %s %s %s %s %s OrderID:%s ClientOrderID:%s Price:%f Amount:%f Executed Amount:%f Remaining Amount:%f",
m.Exchange,
m.AssetType,
m.Pair,
m.Status,
m.Type,
m.Side,
m.ID,
m.ClientOrderID,
m.Price,
m.Amount,
m.ExecutedAmount,
m.RemainingAmount)
}
// printOrderSummary this function will be deprecated when a order manager
// update is done.
func printOrderSummary(m *order.Detail) {
if m == nil {
return
}
log.Debugf(log.WebsocketMgr,
"New Order: %s %s %s %s %s %s OrderID:%s ClientOrderID:%s Price:%f Amount:%f Executed Amount:%f Remaining Amount:%f",
m.Exchange,
m.AssetType,
m.Pair,
m.Status,
m.Type,
m.Side,
m.ID,
m.ClientOrderID,
m.Price,
m.Amount,
m.ExecutedAmount,
m.RemainingAmount)
}
// printAccountHoldingsChangeSummary this function will be deprecated when a
// account holdings update is done.
func printAccountHoldingsChangeSummary(m account.Change) {
log.Debugf(log.WebsocketMgr,
"Account Holdings Balance Changed: %s %s %s has changed balance by %f for account: %s",
m.Exchange,
m.Asset,
m.Currency,
m.Amount,
m.Account)
}

View File

@@ -47,3 +47,12 @@ type Balance struct {
TotalValue float64 TotalValue float64
Hold float64 Hold float64
} }
// Change defines incoming balance change on currency holdings
type Change struct {
Exchange string
Currency currency.Code
Asset asset.Item
Amount float64
Account string
}

View File

@@ -423,7 +423,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error {
Err: err, Err: err,
} }
} }
b.Websocket.DataHandler <- &order.Cancel{ b.Websocket.DataHandler <- &order.Modify{
Price: response.Data[x].Price, Price: response.Data[x].Price,
Amount: response.Data[x].OrderQuantity, Amount: response.Data[x].OrderQuantity,
Exchange: b.Name, Exchange: b.Name,

View File

@@ -304,8 +304,8 @@ func TestGetFee(t *testing.T) {
// CryptocurrencyWithdrawalFee Basic // CryptocurrencyWithdrawalFee Basic
feeBuilder = setFeeBuilder() feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := b.GetFee(feeBuilder); resp != float64(0.00015) || err != nil { if resp, err := b.GetFee(feeBuilder); resp != float64(0.0003) || err != nil {
t.Errorf("Expected: %f, Received: %f", float64(0.00015), resp) t.Errorf("Expected: %f, Received: %f", float64(0.0003), resp)
t.Error(err) t.Error(err)
} }

View File

@@ -238,6 +238,7 @@ const (
Open Status = "OPEN" Open Status = "OPEN"
AutoDeleverage Status = "ADL" AutoDeleverage Status = "ADL"
Closed Status = "CLOSED" Closed Status = "CLOSED"
Pending Status = "PENDING"
) )
// Type enforces a standard for order types across the code base // Type enforces a standard for order types across the code base

View File

@@ -0,0 +1,235 @@
package poloniex
import (
"errors"
"strconv"
"sync"
"github.com/thrasher-corp/gocryptotrader/currency"
)
// CurrencyDetails stores a map of currencies associated with their ID
type CurrencyDetails struct {
pairs map[float64]PairSummaryInfo
codes map[float64]CodeSummaryInfo
// Mutex used for future when we will periodically update this table every
// week or so in production
m sync.RWMutex
}
// PairSummaryInfo defines currency pair information
type PairSummaryInfo struct {
Pair currency.Pair
IsFrozen bool
PostOnly bool
}
// CodeSummaryInfo defines currency information
type CodeSummaryInfo struct {
Currency currency.Code
WithdrawalTXFee float64
MinimumConfirmations int64
DepositAddress string
WithdrawalDepositDisabled bool
Frozen bool
}
var (
errCannotLoadNoData = errors.New("cannot load websocket currency data as data is nil")
errNoDepositAddress = errors.New("no public deposit address for currency")
errPairMapIsNil = errors.New("cannot get currency pair, map is nil")
errCodeMapIsNil = errors.New("cannot get currency code, map is nil")
errCurrencyNotFoundInMap = errors.New("currency not found")
)
// loadPairs loads currency pair associations with unique identifiers from
// ticker data map
func (w *CurrencyDetails) loadPairs(data map[string]Ticker) error {
if data == nil {
return errCannotLoadNoData
}
w.m.Lock()
defer w.m.Unlock()
for k, v := range data {
pair, err := currency.NewPairFromString(k)
if err != nil {
return err
}
if w.pairs == nil {
w.pairs = make(map[float64]PairSummaryInfo)
}
w.pairs[v.ID] = PairSummaryInfo{
Pair: pair,
IsFrozen: v.IsFrozen == 1,
PostOnly: v.PostOnly == 1,
}
}
return nil
}
// loadCodes loads currency codes from currency map
func (w *CurrencyDetails) loadCodes(data map[string]Currencies) error {
if data == nil {
return errCannotLoadNoData
}
w.m.Lock()
defer w.m.Unlock()
for k, v := range data {
if v.Delisted == 1 {
continue
}
if w.codes == nil {
w.codes = make(map[float64]CodeSummaryInfo)
}
w.codes[v.ID] = CodeSummaryInfo{
Currency: currency.NewCode(k),
WithdrawalTXFee: v.TxFee,
MinimumConfirmations: v.MinConfirmations,
DepositAddress: v.DepositAddress,
WithdrawalDepositDisabled: v.WithdrawalDepositDisabled == 1,
Frozen: v.Frozen == 1,
}
}
return nil
}
// GetPair returns a currency pair by its ID
func (w *CurrencyDetails) GetPair(id float64) (currency.Pair, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.pairs == nil {
return currency.Pair{}, errPairMapIsNil
}
p, ok := w.pairs[id]
if ok {
return p.Pair, nil
}
// This is here so we can still log an order with the ID as the currency
// pair which you can then cross reference later with the exchange ID list,
// rather than error out.
op, err := currency.NewPairFromString(strconv.FormatFloat(id, 'f', -1, 64))
if err != nil {
return op, err
}
return op, errIDNotFoundInPairMap
}
// GetCode returns a currency code by its ID
func (w *CurrencyDetails) GetCode(id float64) (currency.Code, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return currency.Code{}, errCodeMapIsNil
}
c, ok := w.codes[id]
if ok {
return c.Currency, nil
}
return currency.Code{}, errIDNotFoundInCodeMap
}
// GetWithdrawalTXFee returns withdrawal transaction fee for the currency
func (w *CurrencyDetails) GetWithdrawalTXFee(c currency.Code) (float64, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return 0, errCodeMapIsNil
}
for _, v := range w.codes {
if v.Currency == c {
return v.WithdrawalTXFee, nil
}
}
return 0, errCurrencyNotFoundInMap
}
// GetDepositAddress returns the public deposit address details for the currency
func (w *CurrencyDetails) GetDepositAddress(c currency.Code) (string, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return "", errCodeMapIsNil
}
for _, v := range w.codes {
if v.Currency == c {
if v.DepositAddress == "" {
return "", errNoDepositAddress
}
return v.DepositAddress, nil
}
}
return "", errCurrencyNotFoundInMap
}
// IsWithdrawAndDepositsEnabled returns if withdrawals or deposits are enabled
func (w *CurrencyDetails) IsWithdrawAndDepositsEnabled(c currency.Code) (bool, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return false, errCodeMapIsNil
}
for _, v := range w.codes {
if v.Currency == c {
return !v.WithdrawalDepositDisabled, nil
}
}
return false, errCurrencyNotFoundInMap
}
// IsTradingEnabledForCurrency returns if the currency is allowed to be traded
func (w *CurrencyDetails) IsTradingEnabledForCurrency(c currency.Code) (bool, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return false, errCodeMapIsNil
}
for _, v := range w.codes {
if v.Currency == c {
return !v.Frozen, nil
}
}
return false, errCurrencyNotFoundInMap
}
// IsTradingEnabledForPair returns if the currency pair is allowed to be traded
func (w *CurrencyDetails) IsTradingEnabledForPair(pair currency.Pair) (bool, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return false, errCodeMapIsNil
}
for _, v := range w.pairs {
if v.Pair.Equal(pair) {
return !v.IsFrozen, nil
}
}
return false, errCurrencyNotFoundInMap
}
// IsPostOnlyForPair returns if an order is allowed to take liquidity from the
// books or reduce positions
func (w *CurrencyDetails) IsPostOnlyForPair(pair currency.Pair) (bool, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return false, errCodeMapIsNil
}
for _, v := range w.pairs {
if v.Pair.Equal(pair) {
return v.PostOnly, nil
}
}
return false, errCurrencyNotFoundInMap
}
// isInitial checks state of maps to determine if they have been loaded or not
func (w *CurrencyDetails) isInitial() bool {
w.m.RLock()
defer w.m.RUnlock()
return w.codes == nil || w.pairs == nil
}

View File

@@ -0,0 +1,209 @@
package poloniex
import (
"errors"
"testing"
"github.com/thrasher-corp/gocryptotrader/currency"
)
func TestWsCurrencyMap(t *testing.T) {
var m CurrencyDetails
if !m.isInitial() {
t.Fatal("unexpected value")
}
err := m.loadPairs(nil)
if !errors.Is(err, errCannotLoadNoData) {
t.Fatalf("expected: %v but received: %v", errCannotLoadNoData, err)
}
err = m.loadCodes(nil)
if !errors.Is(err, errCannotLoadNoData) {
t.Fatalf("expected: %v but received: %v", errCannotLoadNoData, err)
}
_, err = m.GetPair(1337)
if !errors.Is(err, errPairMapIsNil) {
t.Fatalf("expected: %v but received: %v", errPairMapIsNil, err)
}
_, err = m.GetCode(1337)
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.GetWithdrawalTXFee(currency.Code{})
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.GetDepositAddress(currency.Code{})
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsWithdrawAndDepositsEnabled(currency.Code{})
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsTradingEnabledForCurrency(currency.Code{})
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsTradingEnabledForPair(currency.Pair{})
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsPostOnlyForPair(currency.Pair{})
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
c, err := p.GetCurrencies()
if err != nil {
t.Fatal(err)
}
err = m.loadCodes(c)
if err != nil {
t.Fatal(err)
}
tick, err := p.GetTicker()
if err != nil {
t.Fatal(err)
}
err = m.loadPairs(tick)
if err != nil {
t.Fatal(err)
}
pTest, err := m.GetPair(1337)
if !errors.Is(err, errIDNotFoundInPairMap) {
t.Fatalf("expected: %v but received: %v", errIDNotFoundInPairMap, err)
}
if pTest.String() != "1337" {
t.Fatal("unexpected value")
}
_, err = m.GetCode(1337)
if !errors.Is(err, errIDNotFoundInCodeMap) {
t.Fatalf("expected: %v but received: %v", errIDNotFoundInCodeMap, err)
}
btcusdt, err := m.GetPair(121)
if !errors.Is(err, nil) {
t.Fatalf("expected: %v but received: %v", nil, err)
}
if btcusdt.String() != "USDT_BTC" {
t.Fatal("expecting USDT_BTC pair")
}
maid, err := m.GetCode(127)
if !errors.Is(err, nil) {
t.Fatalf("expected: %v but received: %v", nil, err)
}
if maid.String() != "MAID" {
t.Fatal("unexpected value")
}
txFee, err := m.GetWithdrawalTXFee(maid)
if err != nil {
t.Fatal(err)
}
if txFee != 80 {
t.Fatal("unexpected value")
}
_, err = m.GetDepositAddress(maid)
if !errors.Is(err, errNoDepositAddress) {
t.Fatalf("expected: %v but received: %v", errNoDepositAddress, err)
}
dAddr, err := m.GetDepositAddress(currency.NewCode("BCN"))
if !errors.Is(err, nil) {
t.Fatalf("expected: %v but received: %v", nil, err)
}
if dAddr != "25cZNQYVAi3issDCoa6fWA2Aogd4FgPhYdpX3p8KLfhKC6sN8s6Q9WpcW4778TPwcUS5jEM25JrQvjD3XjsvXuNHSWhYUsu" {
t.Fatal("unexpected deposit address")
}
wdEnabled, err := m.IsWithdrawAndDepositsEnabled(maid)
if !errors.Is(err, nil) {
t.Fatalf("expected: %v but received: %v", nil, err)
}
if !wdEnabled {
t.Fatal("unexpected results")
}
tEnabled, err := m.IsTradingEnabledForCurrency(maid)
if !errors.Is(err, nil) {
t.Fatalf("expected: %v but received: %v", nil, err)
}
if !tEnabled {
t.Fatal("unexpected results")
}
cp := currency.NewPair(currency.USDT, currency.BTC)
tEnabled, err = m.IsTradingEnabledForPair(cp)
if !errors.Is(err, nil) {
t.Fatalf("expected: %v but received: %v", nil, err)
}
if !tEnabled {
t.Fatal("unexpected results")
}
postOnly, err := m.IsPostOnlyForPair(cp)
if !errors.Is(err, nil) {
t.Fatalf("expected: %v but received: %v", nil, err)
}
if postOnly {
t.Fatal("unexpected results")
}
_, err = m.GetWithdrawalTXFee(currency.Code{})
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.GetDepositAddress(currency.Code{})
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsWithdrawAndDepositsEnabled(currency.Code{})
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsTradingEnabledForCurrency(currency.Code{})
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsTradingEnabledForPair(currency.Pair{})
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsPostOnlyForPair(currency.Pair{})
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
}

View File

@@ -55,6 +55,7 @@ const (
// Poloniex is the overarching type across the poloniex package // Poloniex is the overarching type across the poloniex package
type Poloniex struct { type Poloniex struct {
exchange.Base exchange.Base
details CurrencyDetails
} }
// GetTicker returns current ticker information // GetTicker returns current ticker information
@@ -258,27 +259,15 @@ func (p *Poloniex) GetBalances() (Balance, error) {
// GetCompleteBalances returns complete balances from your account. // GetCompleteBalances returns complete balances from your account.
func (p *Poloniex) GetCompleteBalances() (CompleteBalances, error) { func (p *Poloniex) GetCompleteBalances() (CompleteBalances, error) {
var result interface{} var result CompleteBalances
vals := url.Values{}
err := p.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, poloniexBalancesComplete, url.Values{}, &result) vals.Set("account", "all")
if err != nil { err := p.SendAuthenticatedHTTPRequest(exchange.RestSpot,
return CompleteBalances{}, err http.MethodPost,
} poloniexBalancesComplete,
vals,
data := result.(map[string]interface{}) &result)
balance := CompleteBalances{} return result, err
balance.Currency = make(map[string]CompleteBalance)
for x, y := range data {
dataVals := y.(map[string]interface{})
balancesData := CompleteBalance{}
balancesData.Available, _ = strconv.ParseFloat(dataVals["available"].(string), 64)
balancesData.OnOrders, _ = strconv.ParseFloat(dataVals["onOrders"].(string), 64)
balancesData.BTCValue, _ = strconv.ParseFloat(dataVals["btcValue"].(string), 64)
balance.Currency[x] = balancesData
}
return balance, nil
} }
// GetDepositAddresses returns deposit addresses for all enabled cryptos. // GetDepositAddresses returns deposit addresses for all enabled cryptos.

View File

@@ -1,6 +1,7 @@
package poloniex package poloniex
import ( import (
"errors"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
@@ -565,7 +566,7 @@ func TestWsSubAck(t *testing.T) {
} }
func TestWsTicker(t *testing.T) { func TestWsTicker(t *testing.T) {
err := p.getCurrencyIDMap() err := p.loadCurrencyDetails()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@@ -577,7 +578,7 @@ func TestWsTicker(t *testing.T) {
} }
func TestWsExchangeVolume(t *testing.T) { func TestWsExchangeVolume(t *testing.T) {
err := p.getCurrencyIDMap() err := p.loadCurrencyDetails()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@@ -589,7 +590,8 @@ func TestWsExchangeVolume(t *testing.T) {
} }
func TestWsTrades(t *testing.T) { func TestWsTrades(t *testing.T) {
err := p.getCurrencyIDMap() p.SetSaveTradeDataStatus(true)
err := p.loadCurrencyDetails()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@@ -601,7 +603,7 @@ func TestWsTrades(t *testing.T) {
} }
func TestWsPriceAggregateOrderbook(t *testing.T) { func TestWsPriceAggregateOrderbook(t *testing.T) {
err := p.getCurrencyIDMap() err := p.loadCurrencyDetails()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@@ -617,25 +619,6 @@ func TestWsPriceAggregateOrderbook(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
func TestWsHandleAccountData(t *testing.T) {
t.Parallel()
err := p.getCurrencyIDMap()
if err != nil {
t.Error(err)
}
jsons := []string{
`[1000,"",[["o",807230187,"0.00000000", "f"],["b",267,"e","0.10000000"]]]`,
`[1000,"",[["n",50,807230187,0,"1000.00000000","0.10000000","2018-11-07 16:42:42"],["b",267,"e","-0.10000000"]]]`,
`[1000,"",[["t", 12345, "0.03000000", "0.50000000", "0.00250000", 0, 6083059, "0.00000375", "2018-09-08 05:54:09", "12345"]]]`,
`[1000,"",[["k", 1337, ""]]]`,
}
for i := range jsons {
err := p.wsHandleData([]byte(jsons[i]))
if err != nil {
t.Error(err)
}
}
}
func TestGetHistoricCandles(t *testing.T) { func TestGetHistoricCandles(t *testing.T) {
currencyPair, err := currency.NewPairFromString("BTC_LTC") currencyPair, err := currency.NewPairFromString("BTC_LTC")
@@ -712,3 +695,330 @@ func TestGetHistoricTrades(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
func TestProcessAccountMarginPosition(t *testing.T) {
err := p.loadCurrencyDetails()
if err != nil {
t.Error(err)
}
margin := []byte(`[1000,"",[["m", 23432933, 28, "-0.06000000"]]]`)
err = p.wsHandleData(margin)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
margin = []byte(`[1000,"",[["m", "23432933", 28, "-0.06000000", null]]]`)
err = p.wsHandleData(margin)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
margin = []byte(`[1000,"",[["m", 23432933, "28", "-0.06000000", null]]]`)
err = p.wsHandleData(margin)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
margin = []byte(`[1000,"",[["m", 23432933, 28, -0.06000000, null]]]`)
err = p.wsHandleData(margin)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
margin = []byte(`[1000,"",[["m", 23432933, 28, "-0.06000000", null]]]`)
err = p.wsHandleData(margin)
if err != nil {
t.Fatal(err)
}
}
func TestProcessAccountPendingOrder(t *testing.T) {
err := p.loadCurrencyDetails()
if err != nil {
t.Error(err)
}
pending := []byte(`[1000,"",[["p",431682155857,127,"1000.00000000","1.00000000","0"]]]`)
err = p.wsHandleData(pending)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
pending = []byte(`[1000,"",[["p","431682155857",127,"1000.00000000","1.00000000","0",null]]]`)
err = p.wsHandleData(pending)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
pending = []byte(`[1000,"",[["p",431682155857,"127","1000.00000000","1.00000000","0",null]]]`)
err = p.wsHandleData(pending)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
pending = []byte(`[1000,"",[["p",431682155857,127,1000.00000000,"1.00000000","0",null]]]`)
err = p.wsHandleData(pending)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
pending = []byte(`[1000,"",[["p",431682155857,127,"1000.00000000",1.00000000,"0",null]]]`)
err = p.wsHandleData(pending)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
pending = []byte(`[1000,"",[["p",431682155857,127,"1000.00000000","1.00000000",0,null]]]`)
err = p.wsHandleData(pending)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
pending = []byte(`[1000,"",[["p",431682155857,127,"1000.00000000","1.00000000","0",null]]]`)
err = p.wsHandleData(pending)
if err != nil {
t.Fatal(err)
}
// Unmatched pair in system
pending = []byte(`[1000,"",[["p",431682155857,666,"1000.00000000","1.00000000","0",null]]]`)
err = p.wsHandleData(pending)
if err != nil {
t.Fatal(err)
}
}
func TestProcessAccountOrderUpdate(t *testing.T) {
orderUpdate := []byte(`[1000,"",[["o",431682155857,"0.00000000","f"]]]`)
err := p.wsHandleData(orderUpdate)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
orderUpdate = []byte(`[1000,"",[["o","431682155857","0.00000000","f",null]]]`)
err = p.wsHandleData(orderUpdate)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
orderUpdate = []byte(`[1000,"",[["o",431682155857,0.00000000,"f",null]]]`)
err = p.wsHandleData(orderUpdate)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
orderUpdate = []byte(`[1000,"",[["o",431682155857,"0.00000000",123,null]]]`)
err = p.wsHandleData(orderUpdate)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
orderUpdate = []byte(`[1000,"",[["o",431682155857,"0.00000000","c",null]]]`)
err = p.wsHandleData(orderUpdate)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
orderUpdate = []byte(`[1000,"",[["o",431682155857,"0.50000000","c",null,"0.50000000"]]]`)
err = p.wsHandleData(orderUpdate)
if err != nil {
t.Fatal(err)
}
orderUpdate = []byte(`[1000,"",[["o",431682155857,"0.00000000","c",null,"1.00000000"]]]`)
err = p.wsHandleData(orderUpdate)
if err != nil {
t.Fatal(err)
}
orderUpdate = []byte(`[1000,"",[["o",431682155857,"0.50000000","f",null]]]`)
err = p.wsHandleData(orderUpdate)
if err != nil {
t.Fatal(err)
}
orderUpdate = []byte(`[1000,"",[["o",431682155857,"0.00000000","s",null]]]`)
err = p.wsHandleData(orderUpdate)
if err != nil {
t.Fatal(err)
}
}
func TestProcessAccountOrderLimit(t *testing.T) {
err := p.loadCurrencyDetails()
if err != nil {
t.Error(err)
}
accountTrade := []byte(`[1000,"",[["n",127,431682155857,"0","1000.00000000","1.00000000","2021-04-13 07:19:56","1.00000000"]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
accountTrade = []byte(`[1000,"",[["n","127",431682155857,"0","1000.00000000","1.00000000","2021-04-13 07:19:56","1.00000000",null]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrade = []byte(`[1000,"",[["n",127,"431682155857","0","1000.00000000","1.00000000","2021-04-13 07:19:56","1.00000000",null]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrade = []byte(`[1000,"",[["n",127,431682155857,0,"1000.00000000","1.00000000","2021-04-13 07:19:56","1.00000000",null]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrade = []byte(`[1000,"",[["n",127,431682155857,"0",1000.00000000,"1.00000000","2021-04-13 07:19:56","1.00000000",null]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrade = []byte(`[1000,"",[["n",127,431682155857,"0","1000.00000000",1.00000000,"2021-04-13 07:19:56","1.00000000",null]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrade = []byte(`[1000,"",[["n",127,431682155857,"0","1000.00000000","1.00000000",1234,"1.00000000",null]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrade = []byte(`[1000,"",[["n",127,431682155857,"0","1000.00000000","1.00000000","2021-04-13 07:19:56",1.00000000,null]]]`)
err = p.wsHandleData(accountTrade)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrade = []byte(`[1000,"",[["n",127,431682155857,"0","1000.00000000","1.00000000","2021-04-13 07:19:56","1.00000000",null]]]`)
err = p.wsHandleData(accountTrade)
if err != nil {
t.Fatal(err)
}
}
func TestProcessAccountBalanceUpdate(t *testing.T) {
err := p.loadCurrencyDetails()
if err != nil {
t.Error(err)
}
balance := []byte(`[1000,"",[["b",243,"e"]]]`)
err = p.wsHandleData(balance)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
balance = []byte(`[1000,"",[["b","243","e","-1.00000000"]]]`)
err = p.wsHandleData(balance)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
balance = []byte(`[1000,"",[["b",243,1234,"-1.00000000"]]]`)
err = p.wsHandleData(balance)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
balance = []byte(`[1000,"",[["b",243,"e",-1.00000000]]]`)
err = p.wsHandleData(balance)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
balance = []byte(`[1000,"",[["b",243,"e","-1.00000000"]]]`)
err = p.wsHandleData(balance)
if err != nil {
t.Fatal(err)
}
}
func TestProcessAccountTrades(t *testing.T) {
accountTrades := []byte(`[1000,"",[["t", 12345, "0.03000000", "0.50000000", "0.00250000", 0, 6083059, "0.00000375", "2018-09-08 05:54:09", "12345"]]]`)
err := p.wsHandleData(accountTrades)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
accountTrades = []byte(`[1000,"",[["t", "12345", "0.03000000", "0.50000000", "0.00250000", 0, 6083059, "0.00000375", "2018-09-08 05:54:09", "12345", "0.015"]]]`)
err = p.wsHandleData(accountTrades)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrades = []byte(`[1000,"",[["t", 12345, 0.03000000, "0.50000000", "0.00250000", 0, 6083059, "0.00000375", "2018-09-08 05:54:09", "12345", "0.015"]]]`)
err = p.wsHandleData(accountTrades)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrades = []byte(`[1000,"",[["t", 12345, "0.03000000", 0.50000000, "0.00250000", 0, 6083059, "0.00000375", "2018-09-08 05:54:09", "12345", "0.015"]]]`)
err = p.wsHandleData(accountTrades)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrades = []byte(`[1000,"",[["t", 12345, "0.03000000", "0.50000000", "0.00250000", 0, 6083059, 0.00000375, "2018-09-08 05:54:09", "12345", "0.015"]]]`)
err = p.wsHandleData(accountTrades)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrades = []byte(`[1000,"",[["t", 12345, "0.03000000", "0.50000000", "0.00250000", 0, 6083059, 0.0000037, "2018-09-08 05:54:09", "12345", "0.015"]]]`)
err = p.wsHandleData(accountTrades)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrades = []byte(`[1000,"",[["t", 12345, "0.03000000", "0.50000000", "0.00250000", 0, 6083059, "0.00000375", 12345, "12345", 0.015]]]`)
err = p.wsHandleData(accountTrades)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
accountTrades = []byte(`[1000,"",[["t", 12345, "0.03000000", "0.50000000", "0.00250000", 0, 6083059, "0.00000375", "2018-09-08 05:54:09", "12345", "0.015"]]]`)
err = p.wsHandleData(accountTrades)
if err != nil {
t.Fatal(err)
}
}
func TestProcessAccountKilledOrder(t *testing.T) {
kill := []byte(`[1000,"",[["k", 1337]]]`)
err := p.wsHandleData(kill)
if !errors.Is(err, errNotEnoughData) {
t.Fatalf("expected: %v but received: %v", errNotEnoughData, err)
}
kill = []byte(`[1000,"",[["k", "1337", null]]]`)
err = p.wsHandleData(kill)
if !errors.Is(err, errTypeAssertionFailure) {
t.Fatalf("expected: %v but received: %v", errTypeAssertionFailure, err)
}
kill = []byte(`[1000,"",[["k", 1337, null]]]`)
err = p.wsHandleData(kill)
if err != nil {
t.Fatal(err)
}
}
func TestGetCompleteBalances(t *testing.T) {
if !mockTests && !areTestAPIKeysSet() {
t.Skip("API keys not set, mockTests false, skipping test")
}
_, err := p.GetCompleteBalances()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -16,9 +16,10 @@ type Ticker struct {
PercentChange float64 `json:"percentChange,string"` PercentChange float64 `json:"percentChange,string"`
BaseVolume float64 `json:"baseVolume,string"` BaseVolume float64 `json:"baseVolume,string"`
QuoteVolume float64 `json:"quoteVolume,string"` QuoteVolume float64 `json:"quoteVolume,string"`
IsFrozen int64 `json:"isFrozen,string"`
High24Hr float64 `json:"high24hr,string"` High24Hr float64 `json:"high24hr,string"`
Low24Hr float64 `json:"low24hr,string"` Low24Hr float64 `json:"low24hr,string"`
IsFrozen uint8 `json:"isFrozen,string"`
PostOnly uint8 `json:"postOnly,string"`
} }
// OrderbookResponseAll holds the full response type orderbook // OrderbookResponseAll holds the full response type orderbook
@@ -27,9 +28,7 @@ type OrderbookResponseAll struct {
} }
// CompleteBalances holds the full balance data // CompleteBalances holds the full balance data
type CompleteBalances struct { type CompleteBalances map[string]CompleteBalance
Currency map[string]CompleteBalance
}
// OrderbookResponse is a sub-type for orderbooks // OrderbookResponse is a sub-type for orderbooks
type OrderbookResponse struct { type OrderbookResponse struct {
@@ -116,15 +115,14 @@ type ChartData struct {
// Currencies contains currency information // Currencies contains currency information
type Currencies struct { type Currencies struct {
ID float64 `json:"id"` ID float64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
MaxDailyWithdrawal string `json:"maxDailyWithdrawal"` TxFee float64 `json:"txFee,string"`
TxFee float64 `json:"txFee,string"` MinConfirmations int64 `json:"minConf"`
MinConfirmations int64 `json:"minConf"` DepositAddress string `json:"depositAddress"`
DepositAddresses interface{} `json:"depositAddress"` WithdrawalDepositDisabled uint8 `json:"disabled"`
Disabled int64 `json:"disabled"` Delisted uint8 `json:"delisted"`
Delisted int64 `json:"delisted"` Frozen uint8 `json:"frozen"`
Frozen int64 `json:"frozen"`
} }
// LoanOrder holds loan order information // LoanOrder holds loan order information
@@ -148,9 +146,9 @@ type Balance struct {
// CompleteBalance contains the complete balance with a btcvalue // CompleteBalance contains the complete balance with a btcvalue
type CompleteBalance struct { type CompleteBalance struct {
Available float64 Available float64 `json:"available,string"`
OnOrders float64 OnOrders float64 `json:"onOrders,string"`
BTCValue float64 BTCValue float64 `json:"btcValue,string"`
} }
// DepositAddresses holds the full address per crypto-currency // DepositAddresses holds the full address per crypto-currency
@@ -442,13 +440,6 @@ var WithdrawalFees = map[currency.Code]float64{
currency.ZEC: 0.001, currency.ZEC: 0.001,
} }
// WsAccountBalanceUpdateResponse Authenticated Ws Account data
type WsAccountBalanceUpdateResponse struct {
currencyID float64
wallet string
amount float64
}
// WsOrderUpdateResponse Authenticated Ws Account data // WsOrderUpdateResponse Authenticated Ws Account data
type WsOrderUpdateResponse struct { type WsOrderUpdateResponse struct {
OrderNumber float64 OrderNumber float64

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff