mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-30 07:26:46 +00:00
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:
@@ -3374,10 +3374,10 @@ func TestUpdateOrderbook(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateAccountInfo(t *testing.T) {
|
||||
func TestUpdateAccountBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
|
||||
result, err := e.UpdateAccountInfo(contextGenerate(), asset.Spot)
|
||||
result, err := e.UpdateAccountBalances(contextGenerate(), asset.Spot)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
}
|
||||
@@ -4009,24 +4009,30 @@ var pushDataMap = map[string]string{
|
||||
"Liquidation Orders": `{"arg": {"channel": "liquidation-orders", "instType": "SWAP" }, "data": [ { "details": [ { "bkLoss": "0", "bkPx": "0.007831", "ccy": "", "posSide": "short", "side": "buy", "sz": "13", "ts": "1692266434010" } ], "instFamily": "IOST-USDT", "instId": "IOST-USDT-SWAP", "instType": "SWAP", "uly": "IOST-USDT"}]}`,
|
||||
"Economic Calendar": `{"arg": {"channel": "economic-calendar" }, "data": [ { "calendarId": "319275", "date": "1597026383085", "region": "United States", "category": "Manufacturing PMI", "event": "S&P Global Manufacturing PMI Final", "refDate": "1597026383085", "actual": "49.2", "previous": "47.3", "forecast": "49.3", "importance": "2", "prevInitial": "", "ccy": "", "unit": "", "ts": "1698648096590" } ] }`,
|
||||
"Failure": `{ "event": "error", "code": "60012", "msg": "Invalid request: {\"op\": \"subscribe\", \"args\":[{ \"channel\" : \"block-tickers\", \"instId\" : \"LTC-USD-200327\"}]}", "connId": "a4d3ae55" }`,
|
||||
"Balance Save Error": `{"arg": {"channel": "balance_and_position","uid": "77982378738415880"},"data": [{"pTime": "1597026383085","eventType": "snapshot","balData": [{"ccy": "BTC","cashBal": "1","uTime": "1597026383085"}],"posData": [{"posId": "1111111111","tradeId": "2","instId": "BTC-USD-191018","instType": "FUTURES","mgnMode": "cross","posSide": "long","pos": "10","ccy": "BTC","posCcy": "","avgPx": "3320","uTIme": "1597026383085"}]}]}`,
|
||||
}
|
||||
|
||||
func TestPushData(t *testing.T) {
|
||||
func TestWsHandleData(t *testing.T) {
|
||||
t.Parallel()
|
||||
e := new(Exchange) //nolint:govet // Intentional shadow
|
||||
require.NoError(t, testexch.Setup(e), "Setup must not error")
|
||||
|
||||
for x := range pushDataMap {
|
||||
if x == "Balance And Position" {
|
||||
for name, msg := range pushDataMap {
|
||||
switch name {
|
||||
case "Balance And Position":
|
||||
e.API.AuthenticatedSupport = true
|
||||
e.API.AuthenticatedWebsocketSupport = true
|
||||
e.SetCredentials("test", "test", "test", "", "", "")
|
||||
} else {
|
||||
default:
|
||||
e.API.AuthenticatedSupport = false
|
||||
e.API.AuthenticatedWebsocketSupport = false
|
||||
}
|
||||
err := e.WsHandleData(t.Context(), []byte(pushDataMap[x]))
|
||||
require.NoErrorf(t, err, "Okx %s error %s", x, err)
|
||||
err := e.WsHandleData(t.Context(), []byte(msg))
|
||||
if name == "Balance Save Error" {
|
||||
assert.ErrorIs(t, err, exchange.ErrAuthenticationSupportNotEnabled, "wsProcessBalanceAndPosition Accounts.Save should error without credentials")
|
||||
} else {
|
||||
require.NoErrorf(t, err, "%s must not error", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3288,9 +3288,9 @@ type PositionDataDetail struct {
|
||||
|
||||
// BalanceData represents currency and it's Cash balance with the update time
|
||||
type BalanceData struct {
|
||||
Currency string `json:"ccy"`
|
||||
CashBalance types.Number `json:"cashBal"`
|
||||
UpdateTime types.Time `json:"uTime"`
|
||||
Currency currency.Code `json:"ccy"`
|
||||
CashBalance types.Number `json:"cashBal"`
|
||||
UpdateTime types.Time `json:"uTime"`
|
||||
}
|
||||
|
||||
// BalanceAndPositionData represents balance and position data with the push time
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
@@ -1459,28 +1459,22 @@ func (e *Exchange) wsProcessBalanceAndPosition(ctx context.Context, data []byte)
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var changes []account.Change
|
||||
subAccts := accounts.SubAccounts{accounts.NewSubAccount(asset.Spot, resp.Argument.UID)}
|
||||
for i := range resp.Data {
|
||||
for j := range resp.Data[i].BalanceData {
|
||||
changes = append(changes, account.Change{
|
||||
AssetType: asset.Spot,
|
||||
Account: resp.Argument.UID,
|
||||
Balance: &account.Balance{
|
||||
Currency: currency.NewCode(resp.Data[i].BalanceData[j].Currency),
|
||||
Total: resp.Data[i].BalanceData[j].CashBalance.Float64(),
|
||||
Free: resp.Data[i].BalanceData[j].CashBalance.Float64(),
|
||||
UpdatedAt: resp.Data[i].BalanceData[j].UpdateTime.Time(),
|
||||
},
|
||||
subAccts[0].Balances.Set(resp.Data[i].BalanceData[j].Currency, accounts.Balance{
|
||||
Total: resp.Data[i].BalanceData[j].CashBalance.Float64(),
|
||||
Free: resp.Data[i].BalanceData[j].CashBalance.Float64(),
|
||||
UpdatedAt: resp.Data[i].BalanceData[j].UpdateTime.Time(),
|
||||
})
|
||||
}
|
||||
// TODO: Handle position data
|
||||
}
|
||||
e.Websocket.DataHandler <- changes
|
||||
return account.ProcessChange(e.Name, changes, creds)
|
||||
if err := e.Accounts.Save(ctx, subAccts, false); err != nil {
|
||||
return err
|
||||
}
|
||||
e.Websocket.DataHandler <- subAccts
|
||||
return nil
|
||||
}
|
||||
|
||||
// wsProcessPushData processes push data coming through the websocket channel
|
||||
|
||||
@@ -16,10 +16,10 @@ 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/order/limits"
|
||||
"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/collateral"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
@@ -614,44 +614,26 @@ func (e *Exchange) UpdateOrderbook(ctx context.Context, pair currency.Pair, asse
|
||||
return orderbook.Get(e.Name, pair, assetType)
|
||||
}
|
||||
|
||||
// UpdateAccountInfo retrieves balances for all enabled currencies.
|
||||
func (e *Exchange) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) {
|
||||
// UpdateAccountBalances retrieves currency balances
|
||||
func (e *Exchange) UpdateAccountBalances(ctx context.Context, assetType asset.Item) (accounts.SubAccounts, error) {
|
||||
if err := e.CurrencyPairs.IsAssetEnabled(assetType); err != nil {
|
||||
return account.Holdings{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var info account.Holdings
|
||||
var acc account.SubAccount
|
||||
info.Exchange = e.Name
|
||||
if !e.SupportsAsset(assetType) {
|
||||
return info, fmt.Errorf("%w: %v", asset.ErrNotSupported, assetType)
|
||||
}
|
||||
accountBalances, err := e.AccountBalance(ctx, currency.EMPTYCODE)
|
||||
resp, err := e.AccountBalance(ctx, currency.EMPTYCODE)
|
||||
if err != nil {
|
||||
return info, err
|
||||
return nil, err
|
||||
}
|
||||
currencyBalances := []account.Balance{}
|
||||
for i := range accountBalances {
|
||||
for j := range accountBalances[i].Details {
|
||||
currencyBalances = append(currencyBalances, account.Balance{
|
||||
Currency: accountBalances[i].Details[j].Currency,
|
||||
Total: accountBalances[i].Details[j].EquityOfCurrency.Float64(),
|
||||
Hold: accountBalances[i].Details[j].FrozenBalance.Float64(),
|
||||
Free: accountBalances[i].Details[j].AvailableBalance.Float64(),
|
||||
subAccts := accounts.SubAccounts{accounts.NewSubAccount(assetType, "")}
|
||||
for i := range resp {
|
||||
for j := range resp[i].Details {
|
||||
subAccts[0].Balances.Set(resp[i].Details[j].Currency, accounts.Balance{
|
||||
Total: resp[i].Details[j].EquityOfCurrency.Float64(),
|
||||
Hold: resp[i].Details[j].FrozenBalance.Float64(),
|
||||
Free: resp[i].Details[j].AvailableBalance.Float64(),
|
||||
})
|
||||
}
|
||||
}
|
||||
acc.Currencies = currencyBalances
|
||||
acc.AssetType = assetType
|
||||
info.Accounts = append(info.Accounts, acc)
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return info, 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 withdrawals
|
||||
@@ -1957,7 +1939,7 @@ func (e *Exchange) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBui
|
||||
|
||||
// ValidateAPICredentials validates current credentials used for wrapper
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user