diff --git a/exchanges/bitmex/bitmex.go b/exchanges/bitmex/bitmex.go index bb081686..c8a35f90 100644 --- a/exchanges/bitmex/bitmex.go +++ b/exchanges/bitmex/bitmex.go @@ -93,6 +93,8 @@ const ( bitmexEndpointUserWalletSummary = "/user/walletSummary" bitmexEndpointUserRequestWithdraw = "/user/requestWithdrawal" + constSatoshiBTC = 1e-08 + // ContractPerpetual perpetual contract type ContractPerpetual = iota // ContractFutures futures contract type @@ -772,10 +774,21 @@ func (b *Bitmex) UserRequestWithdrawal(ctx context.Context, params UserRequestWi func (b *Bitmex) GetWalletInfo(ctx context.Context, currency string) (WalletInfo, error) { var info WalletInfo - return info, b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, + if err := b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bitmexEndpointUserWallet, UserCurrencyParams{Currency: currency}, - &info) + &info); err != nil { + return info, err + } + + // Bitmex has an "interesting" of dealing with currencies, + // for instance XBt is actually BTC but in Satoshi units, + // for sanity purposes apply here a conversion to normalize + // this + // avoid a copy here since this is a big struct + normalizeWalletInfo(&info) + + return info, nil } // GetWalletHistory returns user wallet history transaction data @@ -908,7 +921,7 @@ func (b *Bitmex) CaptureError(resp, reType interface{}) error { } err = json.Unmarshal(marshalled, &Error) - if err == nil { + if err == nil && Error.Error.Name != "" { return fmt.Errorf("bitmex error %s: %s", Error.Error.Name, Error.Error.Message) @@ -947,3 +960,13 @@ func calculateTradingFee(purchasePrice, amount float64, isMaker bool) float64 { return fee * purchasePrice * amount } + +// normalizeWalletInfo converts any non-standard currencies (eg. XBt -> BTC) +func normalizeWalletInfo(w *WalletInfo) { + if w.Currency != "XBt" { + return + } + + w.Currency = "BTC" + w.Amount *= constSatoshiBTC +} diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index 589be78a..fabf4012 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -163,8 +163,8 @@ func TestGetTrollboxChannels(t *testing.T) { func TestGetTrollboxConnectedUsers(t *testing.T) { t.Parallel() _, err := b.GetTrollboxConnectedUsers(context.Background()) - if err == nil { - t.Error("GetTrollboxConnectedUsers() Expected error") + if err != nil { + t.Error("GetTrollboxConnectedUsers() error", err) } } @@ -231,8 +231,8 @@ func TestGetActiveAndIndexInstruments(t *testing.T) { func TestGetActiveIntervals(t *testing.T) { t.Parallel() _, err := b.GetActiveIntervals(context.Background()) - if err == nil { - t.Error("GetActiveIntervals() Expected error") + if err != nil { + t.Error("GetActiveIntervals() error", err) } } @@ -731,13 +731,23 @@ func TestGetAccountInfo(t *testing.T) { if areTestAPIKeysSet() { _, err := b.UpdateAccountInfo(context.Background(), asset.Spot) if err != nil { - t.Error("GetAccountInfo() error", err) + t.Error("GetAccountInfo(spot) error", err) + } + + _, err = b.UpdateAccountInfo(context.Background(), asset.Futures) + if err != nil { + t.Error("GetAccountInfo(futures) error", err) } } else { _, err := b.UpdateAccountInfo(context.Background(), asset.Spot) if err == nil { t.Error("GetAccountInfo() error") } + + _, err = b.UpdateAccountInfo(context.Background(), asset.Futures) + if err == nil { + t.Error("GetAccountInfo(futures) error") + } } } @@ -1134,3 +1144,20 @@ func TestUpdateTickers(t *testing.T) { t.Fatal(err) } } + +func TestCurrencyNormalization(t *testing.T) { + w := &WalletInfo{ + Currency: "XBt", + Amount: 1e+08, + } + + normalizeWalletInfo(w) + + if w.Currency != "BTC" { + t.Errorf("currency mismatch, expected BTC, got %s", w.Currency) + } + + if w.Amount != 1.0 { + t.Errorf("amount mismatch, expected 1.0, got %f", w.Amount) + } +} diff --git a/exchanges/bitmex/bitmex_types.go b/exchanges/bitmex/bitmex_types.go index 4bcb98e9..1877a6ee 100644 --- a/exchanges/bitmex/bitmex_types.go +++ b/exchanges/bitmex/bitmex_types.go @@ -649,7 +649,7 @@ type MinWithdrawalFee struct { type WalletInfo struct { Account int64 `json:"account"` Addr string `json:"addr"` - Amount int64 `json:"amount"` + Amount float64 `json:"amount"` ConfirmedDebit int64 `json:"confirmedDebit"` Currency string `json:"currency"` DeltaAmount int64 `json:"deltaAmount"` diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 8429878e..6980d92c 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "sort" + "strconv" "strings" "sync" "time" @@ -179,6 +180,7 @@ func (b *Bitmex) Setup(exch *config.ExchangeConfig) error { return b.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + URL: bitmexWSURL, }) } @@ -415,27 +417,36 @@ func (b *Bitmex) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType func (b *Bitmex) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) { var info account.Holdings - bal, err := b.GetAllUserMargin(ctx) + userMargins, err := b.GetAllUserMargin(ctx) if err != nil { return info, err } - // Need to update to add Margin/Liquidity availibilty + var accountID string var balances []account.Balance - for i := range bal { + // Need to update to add Margin/Liquidity availability + for i := range userMargins { + accountID = strconv.FormatInt(userMargins[i].Account, 10) + + wallet, err := b.GetWalletInfo(ctx, userMargins[i].Currency) + if err != nil { + continue + } + balances = append(balances, account.Balance{ - CurrencyName: currency.NewCode(bal[i].Currency), - TotalValue: float64(bal[i].WalletBalance), + CurrencyName: currency.NewCode(wallet.Currency), + TotalValue: wallet.Amount, }) } + info.Accounts = append(info.Accounts, + account.SubAccount{ + ID: accountID, + Currencies: balances, + }) info.Exchange = b.Name - info.Accounts = append(info.Accounts, account.SubAccount{ - Currencies: balances, - }) - err = account.Process(&info) - if err != nil { + if err := account.Process(&info); err != nil { return account.Holdings{}, err } diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index f233f49b..37a208f7 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -171,7 +171,7 @@ func TestFLastTradeData(t *testing.T) { func TestFRequestPublicBatchTrades(t *testing.T) { t.Parallel() - cp, err := currency.NewPairFromString("BTC_NW") + cp, err := currency.NewPairFromString("BTC_NQ") if err != nil { t.Error(err) } @@ -211,7 +211,7 @@ func TestFQueryTieredAdjustmentFactor(t *testing.T) { func TestFQueryHisOpenInterest(t *testing.T) { t.Parallel() _, err := h.FQueryHisOpenInterest(context.Background(), - "BTC", "next_week", "60min", "cont", 3) + "BTC", "next_quarter", "60min", "cont", 3) if err != nil { t.Error(err) }