bitmex: normalize account info currencies (#799)

* bitmex: normalize account info currencies

Bitmex has an interesting way of returning BTC balances, it returns them
as XBt and denominated in Satoshis instead. Normalize that still in the
bitmex wrapper by performing the conversion and returning the normal use
BTC currency.

* Change NW contract to NQ

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
Luis Rascão
2021-10-13 01:05:16 +01:00
committed by GitHub
parent a5a960e9df
commit 0a91af0f2e
5 changed files with 82 additions and 21 deletions

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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"`

View File

@@ -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
}

View File

@@ -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)
}