accounts: Move to instance methods, fix races and isolate tests (#1923)

* Bybit: Fix race in TestUpdateAccountInfo and  TestWSHandleData

* DriveBy rename TestWSHandleData
* This doesn't address running with -race=2+ due to the singleton

* Accounts: Add account.GetService()

* exchange: Assertify TestSetupDefaults

* Exchanges: Add account.Service override for testing

* Exchanges: Remove duplicate IsWebsocketEnabled test from TestSetupDefaults

* Dispatch: Replace nil checks with NilGuard

* Engine: Remove deprecated printAccountHoldingsChangeSummary

* Dispatcher: Add EnsureRunning method

* Accounts: Move singleton accounts service to exchange Accounts

* Move singleton accounts service to exchange Accounts

This maintains the concept of a global store, whilst allowing exchanges
to override it when needed, particularly for testing.

APIServer:

* Remove getAllActiveAccounts from apiserver

Deprecated apiserver only thing using this, so remove it instead of
updating it

* Update comment for UpdateAccountBalances everywhere

* Docs: Add punctuation to function comments

* Bybit: Coverage for wsProcessWalletPushData Save
This commit is contained in:
Gareth Kirwan
2025-10-28 09:52:45 +07:00
committed by GitHub
parent bda9bbec66
commit 73e200e4e7
140 changed files with 3515 additions and 4025 deletions

View File

@@ -673,7 +673,7 @@ func (e *Exchange) ConfirmEmail(ctx context.Context, token string) (ConfirmEmail
&confirmation)
}
// ConfirmTwoFactorAuth confirms 2FA for this account.
// ConfirmTwoFactorAuth confirms 2FA for this account
func (e *Exchange) ConfirmTwoFactorAuth(ctx context.Context, token, typ string) (bool, error) {
var working bool
@@ -966,12 +966,14 @@ func calculateTradingFee(purchasePrice, amount float64, isMaker bool) float64 {
return fee * purchasePrice * amount
}
var xbtCurr = currency.NewCode("XBt")
// normalizeWalletInfo converts any non-standard currencies (eg. XBt -> BTC)
func normalizeWalletInfo(w *WalletInfo) {
if w.Currency != "XBt" {
if !w.Currency.Equal(xbtCurr) {
return
}
w.Currency = "BTC"
w.Currency = currency.BTC
w.Amount *= constSatoshiBTC
}

View File

@@ -639,19 +639,17 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
}
func TestUpdateAccountInfo(t *testing.T) {
func TestUpdateAccountBalances(t *testing.T) {
t.Parallel()
if sharedtestvalues.AreAPICredentialsSet(e) {
_, err := e.UpdateAccountInfo(t.Context(), asset.Spot)
_, err := e.UpdateAccountBalances(t.Context(), asset.Spot)
require.NoError(t, err)
_, err = e.UpdateAccountInfo(t.Context(), asset.Futures)
_, err = e.UpdateAccountBalances(t.Context(), asset.Futures)
require.NoError(t, err)
} else {
_, err := e.UpdateAccountInfo(t.Context(), asset.Spot)
_, err := e.UpdateAccountBalances(t.Context(), asset.Spot)
require.Error(t, err)
_, err = e.UpdateAccountInfo(t.Context(), asset.Futures)
_, err = e.UpdateAccountBalances(t.Context(), asset.Futures)
require.Error(t, err)
}
}
@@ -973,13 +971,13 @@ func TestUpdateTickers(t *testing.T) {
func TestNormalizeWalletInfo(t *testing.T) {
w := &WalletInfo{
Currency: "XBt",
Currency: xbtCurr,
Amount: 1e+08,
}
normalizeWalletInfo(w)
assert.Equal(t, "BTC", w.Currency, "Currency should be correct")
assert.Equal(t, currency.BTC, w.Currency, "Currency should be correct")
assert.Equal(t, 1.0, w.Amount, "Amount should be correct")
}

View File

@@ -3,6 +3,7 @@ package bitmex
import (
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
@@ -648,31 +649,31 @@ type MinWithdrawalFee struct {
// WalletInfo wallet information
type WalletInfo struct {
Account int64 `json:"account"`
Addr string `json:"addr"`
Amount float64 `json:"amount"`
ConfirmedDebit int64 `json:"confirmedDebit"`
Currency string `json:"currency"`
DeltaAmount int64 `json:"deltaAmount"`
DeltaDeposited int64 `json:"deltaDeposited"`
DeltaTransferIn int64 `json:"deltaTransferIn"`
DeltaTransferOut int64 `json:"deltaTransferOut"`
DeltaWithdrawn int64 `json:"deltaWithdrawn"`
Deposited int64 `json:"deposited"`
PendingCredit int64 `json:"pendingCredit"`
PendingDebit int64 `json:"pendingDebit"`
PrevAmount int64 `json:"prevAmount"`
PrevDeposited int64 `json:"prevDeposited"`
PrevTimestamp time.Time `json:"prevTimestamp"`
PrevTransferIn int64 `json:"prevTransferIn"`
PrevTransferOut int64 `json:"prevTransferOut"`
PrevWithdrawn int64 `json:"prevWithdrawn"`
Script string `json:"script"`
Timestamp time.Time `json:"timestamp"`
TransferIn int64 `json:"transferIn"`
TransferOut int64 `json:"transferOut"`
WithdrawalLock []string `json:"withdrawalLock"`
Withdrawn int64 `json:"withdrawn"`
Account int64 `json:"account"`
Addr string `json:"addr"`
Amount float64 `json:"amount"`
ConfirmedDebit int64 `json:"confirmedDebit"`
Currency currency.Code `json:"currency"`
DeltaAmount int64 `json:"deltaAmount"`
DeltaDeposited int64 `json:"deltaDeposited"`
DeltaTransferIn int64 `json:"deltaTransferIn"`
DeltaTransferOut int64 `json:"deltaTransferOut"`
DeltaWithdrawn int64 `json:"deltaWithdrawn"`
Deposited int64 `json:"deposited"`
PendingCredit int64 `json:"pendingCredit"`
PendingDebit int64 `json:"pendingDebit"`
PrevAmount int64 `json:"prevAmount"`
PrevDeposited int64 `json:"prevDeposited"`
PrevTimestamp time.Time `json:"prevTimestamp"`
PrevTransferIn int64 `json:"prevTransferIn"`
PrevTransferOut int64 `json:"prevTransferOut"`
PrevWithdrawn int64 `json:"prevWithdrawn"`
Script string `json:"script"`
Timestamp time.Time `json:"timestamp"`
TransferIn int64 `json:"transferIn"`
TransferOut int64 `json:"transferOut"`
WithdrawalLock []string `json:"withdrawalLock"`
Withdrawn int64 `json:"withdrawn"`
}
// orderTypeMap holds order type info based on Bitmex data

View File

@@ -15,9 +15,9 @@ import (
"github.com/thrasher-corp/gocryptotrader/common/key"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
"github.com/thrasher-corp/gocryptotrader/exchange/websocket"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
@@ -442,49 +442,24 @@ func (e *Exchange) UpdateOrderbook(ctx context.Context, p currency.Pair, assetTy
return orderbook.Get(e.Name, p, assetType)
}
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// Bitmex exchange
func (e *Exchange) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
// UpdateAccountBalances retrieves currency balances
func (e *Exchange) UpdateAccountBalances(ctx context.Context, assetType asset.Item) (accounts.SubAccounts, error) {
userMargins, err := e.GetAllUserMargin(ctx)
if err != nil {
return info, err
return nil, err
}
accountBalances := make(map[string][]account.Balance)
var subAccts accounts.SubAccounts
// Need to update to add Margin/Liquidity availability
for i := range userMargins {
accountID := strconv.FormatInt(userMargins[i].Account, 10)
var wallet WalletInfo
wallet, err = e.GetWalletInfo(ctx, userMargins[i].Currency)
wallet, err := e.GetWalletInfo(ctx, userMargins[i].Currency)
if err != nil {
continue
}
accountBalances[accountID] = append(
accountBalances[accountID], account.Balance{
Currency: currency.NewCode(wallet.Currency),
Total: wallet.Amount,
},
)
a := accounts.NewSubAccount(assetType, strconv.FormatInt(userMargins[i].Account, 10))
a.Balances.Set(wallet.Currency, accounts.Balance{Total: wallet.Amount})
subAccts = subAccts.Merge(a)
}
if info.Accounts, err = account.CollectBalances(accountBalances, assetType); err != nil {
return account.Holdings{}, err
}
info.Exchange = e.Name
creds, err := e.GetCredentials(ctx)
if err != nil {
return account.Holdings{}, err
}
if err := account.Process(&info, creds); err != nil {
return account.Holdings{}, err
}
return info, nil
return subAccts, e.Accounts.Save(ctx, subAccts, true)
}
// GetAccountFundingHistory returns funding history, deposits and
@@ -976,10 +951,9 @@ func (e *Exchange) AuthenticateWebsocket(ctx context.Context) error {
return e.websocketSendAuth(ctx)
}
// ValidateAPICredentials validates current credentials used for wrapper
// functionality
// ValidateAPICredentials validates current credentials used for wrapper functionality
func (e *Exchange) ValidateAPICredentials(ctx context.Context, assetType asset.Item) error {
_, err := e.UpdateAccountInfo(ctx, assetType)
_, err := e.UpdateAccountBalances(ctx, assetType)
return e.CheckTransientError(err)
}