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

@@ -15,7 +15,6 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/encoding/json"
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/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
@@ -277,16 +276,13 @@ func (e *Exchange) SendHTTPRequest(ctx context.Context, ep exchange.URL, apiRequ
headers := make(map[string]string)
if authenticated {
var creds *account.Credentials
creds, err = e.GetCredentials(ctx)
creds, err := e.GetCredentials(ctx)
if err != nil {
return nil, err
}
headers["X-USER"] = creds.ClientID
var hmac []byte
hmac, err = crypto.GetHMAC(crypto.HashSHA256,
payload,
[]byte(creds.Key))
hmac, err = crypto.GetHMAC(crypto.HashSHA256, payload, []byte(creds.Key))
if err != nil {
return nil, err
}

View File

@@ -14,9 +14,9 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/core"
"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/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
@@ -347,15 +347,11 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
if apiKey != "" || clientID != "" {
_, err := e.UpdateAccountInfo(t.Context(), asset.Spot)
if err != nil {
t.Error("GetAccountInfo() error", err)
}
_, err := e.UpdateAccountBalances(t.Context(), asset.Spot)
require.NoError(t, err, "UpdateAccountBalances must not error")
} else {
_, err := e.UpdateAccountInfo(t.Context(), asset.Spot)
if err == nil {
t.Error("GetAccountInfo() Expected error")
}
_, err := e.UpdateAccountBalances(t.Context(), asset.Spot)
require.Error(t, err, "UpdateAccountBalances must error")
}
}
@@ -780,8 +776,8 @@ func TestWsLogin(t *testing.T) {
"unverified_email":"",
"username":"test"
}`)
ctx := account.DeployCredentialsToContext(t.Context(),
&account.Credentials{Key: "b46e658f-d4c4-433c-b032-093423b1aaa4", ClientID: "dummy"})
ctx := accounts.DeployCredentialsToContext(t.Context(),
&accounts.Credentials{Key: "b46e658f-d4c4-433c-b032-093423b1aaa4", ClientID: "dummy"})
err := e.wsHandleData(ctx, pressXToJSON)
if err != nil {
t.Error(err)

View File

@@ -12,10 +12,10 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"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"
"github.com/thrasher-corp/gocryptotrader/exchange/websocket/buffer"
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"
@@ -197,100 +197,38 @@ func (e *Exchange) UpdateTradablePairs(ctx context.Context) error {
return e.EnsureOnePairEnabled()
}
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// COINUT 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) (subAccts accounts.SubAccounts, err error) {
var bal *UserBalance
var err error
if e.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
var resp *UserBalance
resp, err = e.wsGetAccountBalance(ctx)
if err != nil {
return info, err
if bal, err = e.wsGetAccountBalance(ctx); err != nil {
return nil, err
}
bal = resp
} else {
bal, err = e.GetUserBalance(ctx)
if err != nil {
return info, err
if bal, err = e.GetUserBalance(ctx); err != nil {
return nil, err
}
}
balances := []account.Balance{
{
Currency: currency.BCH,
Total: bal.BCH,
subAccts = accounts.SubAccounts{&accounts.SubAccount{
AssetType: assetType,
Balances: accounts.CurrencyBalances{
currency.BCH: {Currency: currency.BCH, Total: bal.BCH},
currency.BTC: {Currency: currency.BTC, Total: bal.BTC},
currency.BTG: {Currency: currency.BTG, Total: bal.BTG},
currency.CAD: {Currency: currency.CAD, Total: bal.CAD},
currency.ETC: {Currency: currency.ETC, Total: bal.ETC},
currency.ETH: {Currency: currency.ETH, Total: bal.ETH},
currency.LCH: {Currency: currency.LCH, Total: bal.LCH},
currency.LTC: {Currency: currency.LTC, Total: bal.LTC},
currency.MYR: {Currency: currency.MYR, Total: bal.MYR},
currency.SGD: {Currency: currency.SGD, Total: bal.SGD},
currency.USD: {Currency: currency.USD, Total: bal.USD},
currency.XMR: {Currency: currency.XMR, Total: bal.XMR},
currency.ZEC: {Currency: currency.ZEC, Total: bal.ZEC},
currency.USDT: {Currency: currency.USDT, Total: bal.USDT},
},
{
Currency: currency.BTC,
Total: bal.BTC,
},
{
Currency: currency.BTG,
Total: bal.BTG,
},
{
Currency: currency.CAD,
Total: bal.CAD,
},
{
Currency: currency.ETC,
Total: bal.ETC,
},
{
Currency: currency.ETH,
Total: bal.ETH,
},
{
Currency: currency.LCH,
Total: bal.LCH,
},
{
Currency: currency.LTC,
Total: bal.LTC,
},
{
Currency: currency.MYR,
Total: bal.MYR,
},
{
Currency: currency.SGD,
Total: bal.SGD,
},
{
Currency: currency.USD,
Total: bal.USD,
},
{
Currency: currency.USDT,
Total: bal.USDT,
},
{
Currency: currency.XMR,
Total: bal.XMR,
},
{
Currency: currency.ZEC,
Total: bal.ZEC,
},
}
info.Exchange = e.Name
info.Accounts = append(info.Accounts, account.SubAccount{
AssetType: assetType,
Currencies: balances,
})
creds, err := e.GetCredentials(ctx)
if err != nil {
return account.Holdings{}, err
}
err = account.Process(&info, creds)
if err != nil {
return account.Holdings{}, err
}
return info, nil
}}
return subAccts, e.Accounts.Save(ctx, subAccts, true)
}
// UpdateTickers updates the ticker for all currency pairs of a given asset type
@@ -1035,10 +973,9 @@ func (e *Exchange) loadInstrumentsIfNotLoaded(ctx context.Context) error {
return nil
}
// 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)
}