mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-23 07:26:47 +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:
@@ -629,7 +629,7 @@ func (e *Exchange) GetSpotOrders(ctx context.Context, currencyPair currency.Pair
|
||||
}
|
||||
|
||||
// CancelAllOpenOrdersSpecifiedCurrencyPair cancel all open orders in specified currency pair
|
||||
func (e *Exchange) CancelAllOpenOrdersSpecifiedCurrencyPair(ctx context.Context, currencyPair currency.Pair, side order.Side, account asset.Item) ([]SpotOrder, error) {
|
||||
func (e *Exchange) CancelAllOpenOrdersSpecifiedCurrencyPair(ctx context.Context, currencyPair currency.Pair, side order.Side, a asset.Item) ([]SpotOrder, error) {
|
||||
if currencyPair.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
@@ -638,8 +638,8 @@ func (e *Exchange) CancelAllOpenOrdersSpecifiedCurrencyPair(ctx context.Context,
|
||||
if side == order.Buy || side == order.Sell {
|
||||
params.Set("side", strings.ToLower(side.Title()))
|
||||
}
|
||||
if account == asset.Spot || account == asset.Margin || account == asset.CrossMargin {
|
||||
params.Set("account", account.String())
|
||||
if a == asset.Spot || a == asset.Margin || a == asset.CrossMargin {
|
||||
params.Set("account", a.String())
|
||||
}
|
||||
var response []SpotOrder
|
||||
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelAllOpenOrdersEPL, http.MethodDelete, gateioSpotOrders, params, nil, &response)
|
||||
@@ -663,7 +663,7 @@ func (e *Exchange) CancelBatchOrdersWithIDList(ctx context.Context, args []Cance
|
||||
}
|
||||
|
||||
// GetSpotOrder retrieves a single spot order using the order id and currency pair information.
|
||||
func (e *Exchange) GetSpotOrder(ctx context.Context, orderID string, currencyPair currency.Pair, account asset.Item) (*SpotOrder, error) {
|
||||
func (e *Exchange) GetSpotOrder(ctx context.Context, orderID string, currencyPair currency.Pair, a asset.Item) (*SpotOrder, error) {
|
||||
if orderID == "" {
|
||||
return nil, errInvalidOrderID
|
||||
}
|
||||
@@ -672,7 +672,7 @@ func (e *Exchange) GetSpotOrder(ctx context.Context, orderID string, currencyPai
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("currency_pair", currencyPair.String())
|
||||
if accountType := account.String(); accountType != "" {
|
||||
if accountType := a.String(); accountType != "" {
|
||||
params.Set("account", accountType)
|
||||
}
|
||||
var response *SpotOrder
|
||||
@@ -817,7 +817,7 @@ func (e *Exchange) CreatePriceTriggeredOrder(ctx context.Context, arg *PriceTrig
|
||||
}
|
||||
|
||||
// GetPriceTriggeredOrderList retrieves price orders created with an order detail and trigger price information.
|
||||
func (e *Exchange) GetPriceTriggeredOrderList(ctx context.Context, status string, market currency.Pair, account asset.Item, offset, limit uint64) ([]SpotPriceTriggeredOrder, error) {
|
||||
func (e *Exchange) GetPriceTriggeredOrderList(ctx context.Context, status string, market currency.Pair, a asset.Item, offset, limit uint64) ([]SpotPriceTriggeredOrder, error) {
|
||||
if status != statusOpen && status != statusFinished {
|
||||
return nil, fmt.Errorf("%w status %s", errInvalidOrderStatus, status)
|
||||
}
|
||||
@@ -826,8 +826,8 @@ func (e *Exchange) GetPriceTriggeredOrderList(ctx context.Context, status string
|
||||
if market.IsPopulated() {
|
||||
params.Set("market", market.String())
|
||||
}
|
||||
if account == asset.CrossMargin {
|
||||
params.Set("account", account.String())
|
||||
if a == asset.CrossMargin {
|
||||
params.Set("account", a.String())
|
||||
}
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatUint(limit, 10))
|
||||
@@ -840,18 +840,18 @@ func (e *Exchange) GetPriceTriggeredOrderList(ctx context.Context, status string
|
||||
}
|
||||
|
||||
// CancelMultipleSpotOpenOrders deletes price triggered orders.
|
||||
func (e *Exchange) CancelMultipleSpotOpenOrders(ctx context.Context, currencyPair currency.Pair, account asset.Item) ([]SpotPriceTriggeredOrder, error) {
|
||||
func (e *Exchange) CancelMultipleSpotOpenOrders(ctx context.Context, currencyPair currency.Pair, a asset.Item) ([]SpotPriceTriggeredOrder, error) {
|
||||
params := url.Values{}
|
||||
if currencyPair.IsPopulated() {
|
||||
params.Set("market", currencyPair.String())
|
||||
}
|
||||
switch account {
|
||||
switch a {
|
||||
case asset.Empty:
|
||||
return nil, asset.ErrNotSupported
|
||||
case asset.Spot:
|
||||
params.Set("account", "normal")
|
||||
default:
|
||||
params.Set("account", account.String())
|
||||
params.Set("account", a.String())
|
||||
}
|
||||
var response []SpotPriceTriggeredOrder
|
||||
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelTriggerOrdersEPL, http.MethodDelete, gateioSpotPriceOrders, params, nil, &response)
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"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/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/futures"
|
||||
@@ -66,12 +66,35 @@ func TestUpdateTradablePairs(t *testing.T) {
|
||||
testexch.UpdatePairsOnce(t, e)
|
||||
}
|
||||
|
||||
func TestGetAccountInfo(t *testing.T) {
|
||||
func TestCancelAllExchangeOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
_, err := e.CancelAllOrders(t.Context(), nil)
|
||||
require.ErrorIs(t, err, order.ErrCancelOrderIsNil)
|
||||
|
||||
r := &order.Cancel{
|
||||
OrderID: "1",
|
||||
AccountID: "1",
|
||||
}
|
||||
|
||||
for _, a := range e.GetAssetTypes(false) {
|
||||
r.AssetType = a
|
||||
r.Pair = currency.EMPTYPAIR
|
||||
_, err = e.CancelAllOrders(t.Context(), r)
|
||||
assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
|
||||
|
||||
r.Pair = getPair(t, a)
|
||||
_, err = e.CancelAllOrders(t.Context(), r)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
|
||||
for _, a := range e.GetAssetTypes(false) {
|
||||
_, err := e.UpdateAccountInfo(t.Context(), a)
|
||||
assert.NoErrorf(t, err, "UpdateAccountInfo should not error for asset %s", a)
|
||||
_, err := e.UpdateAccountBalances(t.Context(), a)
|
||||
assert.NoErrorf(t, err, "UpdateAccountBalances should not error for asset %s", a)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2028,7 +2051,7 @@ const wsBalancesPushDataJSON = `{"time": 1605248616, "channel": "spot.balances",
|
||||
|
||||
func TestBalancesPushData(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := account.DeployCredentialsToContext(t.Context(), &account.Credentials{Key: "test", Secret: "test"})
|
||||
ctx := accounts.DeployCredentialsToContext(t.Context(), &accounts.Credentials{Key: "test", Secret: "test"})
|
||||
if err := e.WsHandleSpotData(ctx, nil, []byte(wsBalancesPushDataJSON)); err != nil {
|
||||
t.Errorf("%s websocket balances push data error: %v", e.Name, err)
|
||||
}
|
||||
@@ -2047,7 +2070,7 @@ const wsCrossMarginBalancePushDataJSON = `{"time": 1605248616,"channel": "spot.c
|
||||
|
||||
func TestCrossMarginBalancePushData(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := account.DeployCredentialsToContext(t.Context(), &account.Credentials{Key: "test", Secret: "test"})
|
||||
ctx := accounts.DeployCredentialsToContext(t.Context(), &accounts.Credentials{Key: "test", Secret: "test"})
|
||||
if err := e.WsHandleSpotData(ctx, nil, []byte(wsCrossMarginBalancePushDataJSON)); err != nil {
|
||||
t.Errorf("%s websocket cross margin balance push data error: %v", e.Name, err)
|
||||
}
|
||||
@@ -2069,7 +2092,7 @@ func TestFuturesDataHandler(t *testing.T) {
|
||||
require.NoError(t, testexch.Setup(e), "Test instance Setup must not error")
|
||||
testexch.FixtureToDataHandler(t, "testdata/wsFutures.json", func(ctx context.Context, m []byte) error {
|
||||
if strings.Contains(string(m), "futures.balances") {
|
||||
ctx = account.DeployCredentialsToContext(ctx, &account.Credentials{Key: "test", Secret: "test"})
|
||||
ctx = accounts.DeployCredentialsToContext(ctx, &accounts.Credentials{Key: "test", Secret: "test"})
|
||||
}
|
||||
return e.WsHandleFuturesData(ctx, nil, m, asset.CoinMarginedFutures)
|
||||
})
|
||||
@@ -2236,7 +2259,7 @@ const optionsBalancePushDataJSON = `{ "channel": "options.balances", "event": "u
|
||||
|
||||
func TestOptionsBalancePushData(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := account.DeployCredentialsToContext(t.Context(), &account.Credentials{Key: "test", Secret: "test"})
|
||||
ctx := accounts.DeployCredentialsToContext(t.Context(), &accounts.Credentials{Key: "test", Secret: "test"})
|
||||
if err := e.WsHandleOptionsData(ctx, nil, []byte(optionsBalancePushDataJSON)); err != nil {
|
||||
t.Errorf("%s websocket options balance push data error: %v", e.Name, err)
|
||||
}
|
||||
|
||||
@@ -949,18 +949,18 @@ type OptionsUnderlyingTicker struct {
|
||||
IndexPrice types.Number `json:"index_price"`
|
||||
}
|
||||
|
||||
// OptionAccount represents option account.
|
||||
// OptionAccount represents an option account.
|
||||
type OptionAccount struct {
|
||||
User int64 `json:"user"`
|
||||
Currency string `json:"currency"`
|
||||
ShortEnabled bool `json:"short_enabled"`
|
||||
Total types.Number `json:"total"`
|
||||
UnrealisedPnl string `json:"unrealised_pnl"`
|
||||
InitMargin string `json:"init_margin"`
|
||||
MaintMargin string `json:"maint_margin"`
|
||||
OrderMargin string `json:"order_margin"`
|
||||
Available types.Number `json:"available"`
|
||||
Point string `json:"point"`
|
||||
User int64 `json:"user"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
ShortEnabled bool `json:"short_enabled"`
|
||||
Total types.Number `json:"total"`
|
||||
UnrealisedPnl string `json:"unrealised_pnl"`
|
||||
InitMargin string `json:"init_margin"`
|
||||
MaintMargin string `json:"maint_margin"`
|
||||
OrderMargin string `json:"order_margin"`
|
||||
Available types.Number `json:"available"`
|
||||
Point string `json:"point"`
|
||||
}
|
||||
|
||||
// AccountBook represents account changing history item
|
||||
@@ -1218,11 +1218,11 @@ type MarginAccountItem struct {
|
||||
|
||||
// AccountBalanceInformation represents currency account balance information.
|
||||
type AccountBalanceInformation struct {
|
||||
Available types.Number `json:"available"`
|
||||
Borrowed types.Number `json:"borrowed"`
|
||||
Interest types.Number `json:"interest"`
|
||||
Currency string `json:"currency"`
|
||||
LockedAmount types.Number `json:"locked"`
|
||||
Available types.Number `json:"available"`
|
||||
Borrowed types.Number `json:"borrowed"`
|
||||
Interest types.Number `json:"interest"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
LockedAmount types.Number `json:"locked"`
|
||||
}
|
||||
|
||||
// MarginAccountBalanceChangeInfo represents margin account balance
|
||||
@@ -1371,9 +1371,9 @@ type SpotTradingFeeRate struct {
|
||||
|
||||
// SpotAccount represents spot account
|
||||
type SpotAccount struct {
|
||||
Currency string `json:"currency"`
|
||||
Available types.Number `json:"available"`
|
||||
Locked types.Number `json:"locked"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
Available types.Number `json:"available"`
|
||||
Locked types.Number `json:"locked"`
|
||||
}
|
||||
|
||||
// CreateOrderRequest represents a single order creation param.
|
||||
@@ -1710,26 +1710,26 @@ type InitFlashSwapOrderPreviewResponse struct {
|
||||
|
||||
// FuturesAccount represents futures account detail
|
||||
type FuturesAccount struct {
|
||||
User int64 `json:"user"`
|
||||
Currency string `json:"currency"`
|
||||
Total types.Number `json:"total"` // total = position_margin + order_margin + available
|
||||
UnrealisedPnl types.Number `json:"unrealised_pnl"`
|
||||
PositionMargin types.Number `json:"position_margin"`
|
||||
OrderMargin types.Number `json:"order_margin"` // Order margin of unfinished orders
|
||||
Available types.Number `json:"available"` // The available balance for transferring or trading
|
||||
Point types.Number `json:"point"`
|
||||
Bonus string `json:"bonus"`
|
||||
EnabledCredit bool `json:"enable_credit"`
|
||||
InDualMode bool `json:"in_dual_mode"` // Whether dual mode is enabled
|
||||
UpdateTime types.Time `json:"update_time"`
|
||||
UpdateID int64 `json:"update_id"`
|
||||
PositionInitialMargine types.Number `json:"position_initial_margin"` // applicable to the portfolio margin account model
|
||||
MaintenanceMargin types.Number `json:"maintenance_margin"`
|
||||
MarginMode int64 `json:"margin_mode"` // Margin mode: 1-cross margin, 2-isolated margin, 3-portfolio margin
|
||||
EnabledEvolvedClassic bool `json:"enable_evolved_classic"`
|
||||
CrossInitialMargin types.Number `json:"cross_initial_margin"`
|
||||
CrossUnrealisedPnl types.Number `json:"cross_unrealised_pnl"`
|
||||
IsolatedPositionMargin types.Number `json:"isolated_position_margin"`
|
||||
User int64 `json:"user"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
Total types.Number `json:"total"` // total = position_margin + order_margin + available
|
||||
UnrealisedPnl types.Number `json:"unrealised_pnl"`
|
||||
PositionMargin types.Number `json:"position_margin"`
|
||||
OrderMargin types.Number `json:"order_margin"` // Order margin of unfinished orders
|
||||
Available types.Number `json:"available"` // The available balance for transferring or trading
|
||||
Point types.Number `json:"point"`
|
||||
Bonus string `json:"bonus"`
|
||||
EnabledCredit bool `json:"enable_credit"`
|
||||
InDualMode bool `json:"in_dual_mode"` // Whether dual mode is enabled
|
||||
UpdateTime types.Time `json:"update_time"`
|
||||
UpdateID int64 `json:"update_id"`
|
||||
PositionInitialMargine types.Number `json:"position_initial_margin"` // applicable to the portfolio margin account model
|
||||
MaintenanceMargin types.Number `json:"maintenance_margin"`
|
||||
MarginMode int64 `json:"margin_mode"` // Margin mode: 1-cross margin, 2-isolated margin, 3-portfolio margin
|
||||
EnabledEvolvedClassic bool `json:"enable_evolved_classic"`
|
||||
CrossInitialMargin types.Number `json:"cross_initial_margin"`
|
||||
CrossUnrealisedPnl types.Number `json:"cross_unrealised_pnl"`
|
||||
IsolatedPositionMargin types.Number `json:"isolated_position_margin"`
|
||||
History struct {
|
||||
DepositAndWithdrawal string `json:"dnw"` // total amount of deposit and withdraw
|
||||
ProfitAndLoss types.Number `json:"pnl"` // total amount of trading profit and loss
|
||||
@@ -2159,15 +2159,15 @@ type WsSpotBalance struct {
|
||||
|
||||
// WsMarginBalance represents margin account balance push data
|
||||
type WsMarginBalance struct {
|
||||
Timestamp types.Time `json:"timestamp_ms"`
|
||||
User string `json:"user"`
|
||||
CurrencyPair string `json:"currency_pair"`
|
||||
Currency string `json:"currency"`
|
||||
Change types.Number `json:"change"`
|
||||
Available types.Number `json:"available"`
|
||||
Freeze types.Number `json:"freeze"`
|
||||
Borrowed string `json:"borrowed"`
|
||||
Interest string `json:"interest"`
|
||||
Timestamp types.Time `json:"timestamp_ms"`
|
||||
User string `json:"user"`
|
||||
CurrencyPair string `json:"currency_pair"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
Change types.Number `json:"change"`
|
||||
Available types.Number `json:"available"`
|
||||
Freeze types.Number `json:"freeze"`
|
||||
Borrowed string `json:"borrowed"`
|
||||
Interest string `json:"interest"`
|
||||
}
|
||||
|
||||
// WsFundingBalance represents funding balance push data.
|
||||
@@ -2182,12 +2182,12 @@ type WsFundingBalance struct {
|
||||
|
||||
// WsCrossMarginBalance represents a cross margin balance detail
|
||||
type WsCrossMarginBalance struct {
|
||||
Timestamp types.Time `json:"timestamp_ms"`
|
||||
User string `json:"user"`
|
||||
Currency string `json:"currency"`
|
||||
Change string `json:"change"`
|
||||
Total types.Number `json:"total"`
|
||||
Available types.Number `json:"available"`
|
||||
Timestamp types.Time `json:"timestamp_ms"`
|
||||
User string `json:"user"`
|
||||
Currency currency.Code `json:"currency"`
|
||||
Change string `json:"change"`
|
||||
Total types.Number `json:"total"`
|
||||
Available types.Number `json:"available"`
|
||||
}
|
||||
|
||||
// WsCrossMarginLoan represents a cross margin loan push data
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common/key"
|
||||
"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/fill"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
@@ -489,65 +489,55 @@ func (e *Exchange) processUserPersonalTrades(data []byte) error {
|
||||
}
|
||||
|
||||
func (e *Exchange) processSpotBalances(ctx context.Context, data []byte) error {
|
||||
var resp []WsSpotBalance
|
||||
var resp []*WsSpotBalance
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
subAccts := accounts.SubAccounts{}
|
||||
for _, bal := range resp {
|
||||
a := accounts.NewSubAccount(asset.Spot, bal.User)
|
||||
a.Balances.Set(bal.Currency, accounts.Balance{
|
||||
Total: bal.Total.Float64(),
|
||||
Free: bal.Available.Float64(),
|
||||
Hold: bal.Freeze.Float64(),
|
||||
AvailableWithoutBorrow: bal.Available.Float64(),
|
||||
UpdatedAt: bal.Timestamp.Time(),
|
||||
})
|
||||
subAccts = subAccts.Merge(a)
|
||||
}
|
||||
if err := e.Accounts.Save(ctx, subAccts, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changes := make([]account.Change, len(resp))
|
||||
for i := range resp {
|
||||
changes[i] = account.Change{
|
||||
Account: resp[i].User,
|
||||
AssetType: asset.Spot,
|
||||
Balance: &account.Balance{
|
||||
Currency: resp[i].Currency,
|
||||
Total: resp[i].Total.Float64(),
|
||||
Free: resp[i].Available.Float64(),
|
||||
Hold: resp[i].Freeze.Float64(),
|
||||
AvailableWithoutBorrow: resp[i].Available.Float64(),
|
||||
UpdatedAt: resp[i].Timestamp.Time(),
|
||||
},
|
||||
}
|
||||
}
|
||||
e.Websocket.DataHandler <- changes
|
||||
return account.ProcessChange(e.Name, changes, creds)
|
||||
e.Websocket.DataHandler <- subAccts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Exchange) processMarginBalances(ctx context.Context, data []byte) error {
|
||||
resp := struct {
|
||||
Time types.Time `json:"time"`
|
||||
Channel string `json:"channel"`
|
||||
Event string `json:"event"`
|
||||
Result []WsMarginBalance `json:"result"`
|
||||
Time types.Time `json:"time"`
|
||||
Channel string `json:"channel"`
|
||||
Event string `json:"event"`
|
||||
Result []*WsMarginBalance `json:"result"`
|
||||
}{}
|
||||
err := json.Unmarshal(data, &resp)
|
||||
if err != nil {
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
subAccts := accounts.SubAccounts{}
|
||||
for _, bal := range resp.Result {
|
||||
a := accounts.NewSubAccount(asset.Margin, bal.User)
|
||||
a.Balances.Set(bal.Currency, accounts.Balance{
|
||||
Total: bal.Available.Float64() + bal.Freeze.Float64(),
|
||||
Free: bal.Available.Float64(),
|
||||
Hold: bal.Freeze.Float64(),
|
||||
UpdatedAt: bal.Timestamp.Time(),
|
||||
})
|
||||
subAccts = subAccts.Merge(a)
|
||||
}
|
||||
if err := e.Accounts.Save(ctx, subAccts, false); err != nil {
|
||||
return err
|
||||
}
|
||||
changes := make([]account.Change, len(resp.Result))
|
||||
for x := range resp.Result {
|
||||
changes[x] = account.Change{
|
||||
AssetType: asset.Margin,
|
||||
Balance: &account.Balance{
|
||||
Currency: currency.NewCode(resp.Result[x].Currency),
|
||||
Total: resp.Result[x].Available.Float64() + resp.Result[x].Freeze.Float64(),
|
||||
Free: resp.Result[x].Available.Float64(),
|
||||
Hold: resp.Result[x].Freeze.Float64(),
|
||||
UpdatedAt: resp.Result[x].Timestamp.Time(),
|
||||
},
|
||||
}
|
||||
}
|
||||
e.Websocket.DataHandler <- changes
|
||||
return account.ProcessChange(e.Name, changes, creds)
|
||||
e.Websocket.DataHandler <- subAccts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Exchange) processFundingBalances(data []byte) error {
|
||||
@@ -567,34 +557,30 @@ func (e *Exchange) processFundingBalances(data []byte) error {
|
||||
|
||||
func (e *Exchange) processCrossMarginBalance(ctx context.Context, data []byte) error {
|
||||
resp := struct {
|
||||
Time types.Time `json:"time"`
|
||||
Channel string `json:"channel"`
|
||||
Event string `json:"event"`
|
||||
Result []WsCrossMarginBalance `json:"result"`
|
||||
Time types.Time `json:"time"`
|
||||
Channel string `json:"channel"`
|
||||
Event string `json:"event"`
|
||||
Result []*WsCrossMarginBalance `json:"result"`
|
||||
}{}
|
||||
err := json.Unmarshal(data, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
subAccts := accounts.SubAccounts{}
|
||||
for _, bal := range resp.Result {
|
||||
a := accounts.NewSubAccount(asset.CrossMargin, bal.User)
|
||||
a.Balances.Set(bal.Currency, accounts.Balance{
|
||||
Total: bal.Total.Float64(),
|
||||
Free: bal.Available.Float64(),
|
||||
UpdatedAt: bal.Timestamp.Time(),
|
||||
})
|
||||
subAccts = subAccts.Merge(a)
|
||||
}
|
||||
if err := e.Accounts.Save(ctx, subAccts, false); err != nil {
|
||||
return err
|
||||
}
|
||||
changes := make([]account.Change, len(resp.Result))
|
||||
for x := range resp.Result {
|
||||
changes[x] = account.Change{
|
||||
Account: resp.Result[x].User,
|
||||
AssetType: asset.Margin,
|
||||
Balance: &account.Balance{
|
||||
Currency: currency.NewCode(resp.Result[x].Currency),
|
||||
Total: resp.Result[x].Total.Float64(),
|
||||
Free: resp.Result[x].Available.Float64(),
|
||||
UpdatedAt: resp.Result[x].Timestamp.Time(),
|
||||
},
|
||||
}
|
||||
}
|
||||
e.Websocket.DataHandler <- changes
|
||||
return account.ProcessChange(e.Name, changes, creds)
|
||||
e.Websocket.DataHandler <- subAccts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Exchange) processCrossMarginLoans(data []byte) error {
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
gws "github.com/gorilla/websocket"
|
||||
"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/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
|
||||
@@ -115,7 +115,7 @@ func (e *Exchange) generateDeliveryFuturesPayload(ctx context.Context, event str
|
||||
if len(channelsToSubscribe) == 0 {
|
||||
return nil, errors.New("cannot generate payload, no channels supplied")
|
||||
}
|
||||
var creds *account.Credentials
|
||||
var creds *accounts.Credentials
|
||||
var err error
|
||||
if e.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
creds, err = e.GetCredentials(ctx)
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
gws "github.com/gorilla/websocket"
|
||||
"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/fill"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
@@ -198,7 +198,7 @@ func (e *Exchange) generateFuturesPayload(ctx context.Context, event string, cha
|
||||
if len(channelsToSubscribe) == 0 {
|
||||
return nil, errors.New("cannot generate payload, no channels supplied")
|
||||
}
|
||||
var creds *account.Credentials
|
||||
var creds *accounts.Credentials
|
||||
var err error
|
||||
if e.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
creds, err = e.GetCredentials(ctx)
|
||||
@@ -632,36 +632,31 @@ func (e *Exchange) processPositionCloseData(data []byte) error {
|
||||
}
|
||||
|
||||
func (e *Exchange) processBalancePushData(ctx context.Context, data []byte, assetType asset.Item) error {
|
||||
var resp []WsBalance
|
||||
var resp []*WsBalance
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changes := make([]account.Change, len(resp))
|
||||
for x, bal := range resp {
|
||||
subAccts := accounts.SubAccounts{}
|
||||
for _, bal := range resp {
|
||||
c := bal.Currency
|
||||
if assetType == asset.Options && c.IsEmpty() {
|
||||
c = currency.USDT // Settlement currency is USDT
|
||||
}
|
||||
changes[x] = account.Change{
|
||||
AssetType: assetType,
|
||||
Account: bal.User,
|
||||
Balance: &account.Balance{
|
||||
Currency: c,
|
||||
Total: bal.Balance,
|
||||
Free: bal.Balance,
|
||||
AvailableWithoutBorrow: bal.Balance,
|
||||
UpdatedAt: bal.Time.Time(),
|
||||
},
|
||||
}
|
||||
a := accounts.NewSubAccount(assetType, bal.User)
|
||||
a.Balances.Set(c, accounts.Balance{
|
||||
Total: bal.Balance,
|
||||
Free: bal.Balance,
|
||||
AvailableWithoutBorrow: bal.Balance,
|
||||
UpdatedAt: bal.Time.Time(),
|
||||
})
|
||||
subAccts = subAccts.Merge(a)
|
||||
}
|
||||
e.Websocket.DataHandler <- changes
|
||||
return account.ProcessChange(e.Name, changes, creds)
|
||||
err := e.Accounts.Save(ctx, subAccts, false)
|
||||
if err == nil {
|
||||
e.Websocket.DataHandler <- subAccts
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Exchange) processFuturesReduceRiskLimitNotification(data []byte) error {
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
gws "github.com/gorilla/websocket"
|
||||
"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/fill"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
@@ -218,7 +218,7 @@ func (e *Exchange) generateOptionsPayload(ctx context.Context, event string, cha
|
||||
continue
|
||||
}
|
||||
params = append([]string{strconv.FormatInt(userID, 10)}, params...)
|
||||
var creds *account.Credentials
|
||||
var creds *accounts.Credentials
|
||||
creds, err = e.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -2,6 +2,9 @@ package gateio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
"slices"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -9,8 +12,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
)
|
||||
|
||||
@@ -42,17 +45,17 @@ type websocketBalancesTest struct {
|
||||
input []byte
|
||||
err error
|
||||
deployCreds bool
|
||||
expected []account.Change
|
||||
expected accounts.SubAccounts
|
||||
}
|
||||
|
||||
func TestProcessSpotBalances(t *testing.T) {
|
||||
func TestProcessSpotBalances(t *testing.T) { //nolint:tparallel // Sequential tests, do not use t.Parallel(); Some timestamps are deliberately identical from trading activity
|
||||
t.Parallel()
|
||||
e := new(Exchange) //nolint:govet // Intentional shadow
|
||||
e.SetDefaults()
|
||||
e.Name = "ProcessSpotBalancesTest"
|
||||
e.Accounts = accounts.MustNewAccounts(e)
|
||||
|
||||
// Sequential tests, do not use t.Run(); Some timestamps are deliberately identical from trading activity
|
||||
for _, tc := range []websocketBalancesTest{
|
||||
for i, tc := range []websocketBalancesTest{
|
||||
{
|
||||
input: []byte(`[{"timestamp":"1755718222"}]`),
|
||||
err: exchange.ErrCredentialsAreEmpty,
|
||||
@@ -60,17 +63,19 @@ func TestProcessSpotBalances(t *testing.T) {
|
||||
{
|
||||
deployCreds: true,
|
||||
input: []byte(`[{"timestamp":"1755718222","timestamp_ms":"1755718222394","user":"12870774","currency":"USDT","change":"0","total":"3087.01142272991036062136","available":"3081.68642272991036062136","freeze":"5.325","freeze_change":"5.32500000000000000000","change_type":"order-create"}]`),
|
||||
expected: []account.Change{
|
||||
expected: accounts.SubAccounts{
|
||||
{
|
||||
Account: "12870774",
|
||||
ID: "12870774",
|
||||
AssetType: asset.Spot,
|
||||
Balance: &account.Balance{
|
||||
Currency: currency.USDT,
|
||||
Total: 3087.01142272991036062136,
|
||||
Free: 3081.68642272991036062136,
|
||||
Hold: 5.325,
|
||||
AvailableWithoutBorrow: 3081.68642272991036062136,
|
||||
UpdatedAt: time.UnixMilli(1755718222394),
|
||||
Balances: accounts.CurrencyBalances{
|
||||
currency.USDT: accounts.Balance{
|
||||
Currency: currency.USDT,
|
||||
Total: 3087.01142272991036062136,
|
||||
Free: 3081.68642272991036062136,
|
||||
Hold: 5.325,
|
||||
AvailableWithoutBorrow: 3081.68642272991036062136,
|
||||
UpdatedAt: time.UnixMilli(1755718222394),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -78,44 +83,51 @@ func TestProcessSpotBalances(t *testing.T) {
|
||||
{
|
||||
deployCreds: true,
|
||||
input: []byte(`[{"timestamp":"1755718222","timestamp_ms":"1755718222394","user":"12870774","currency":"USDT","change":"-3.99375000000000000000","total":"3083.01767272991036062136","available":"3081.68642272991036062136","freeze":"1.33125","freeze_change":"-3.99375000000000000000","change_type":"order-match"}]`),
|
||||
expected: []account.Change{
|
||||
expected: accounts.SubAccounts{
|
||||
{
|
||||
Account: "12870774",
|
||||
ID: "12870774",
|
||||
AssetType: asset.Spot,
|
||||
Balance: &account.Balance{
|
||||
Currency: currency.USDT,
|
||||
Total: 3083.01767272991036062136,
|
||||
Free: 3081.68642272991036062136,
|
||||
Hold: 1.33125,
|
||||
AvailableWithoutBorrow: 3081.68642272991036062136,
|
||||
UpdatedAt: time.UnixMilli(1755718222394),
|
||||
Balances: accounts.CurrencyBalances{
|
||||
currency.USDT: accounts.Balance{
|
||||
Currency: currency.USDT,
|
||||
Total: 3083.01767272991036062136,
|
||||
Free: 3081.68642272991036062136,
|
||||
Hold: 1.33125,
|
||||
AvailableWithoutBorrow: 3081.68642272991036062136,
|
||||
UpdatedAt: time.UnixMilli(1755718222394),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
ctx := t.Context()
|
||||
if tc.deployCreds {
|
||||
ctx = account.DeployCredentialsToContext(ctx, &account.Credentials{Key: "test", Secret: "test"})
|
||||
}
|
||||
err := e.processSpotBalances(ctx, tc.input)
|
||||
if tc.err != nil {
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err, "processSpotBalances must not error")
|
||||
checkAccountChange(ctx, t, e, &tc)
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
// Sequential tests, do not use t.Parallel(); Some timestamps are deliberately identical from trading activity
|
||||
ctx := t.Context()
|
||||
if tc.deployCreds {
|
||||
ctx = accounts.DeployCredentialsToContext(ctx, &accounts.Credentials{Key: "test", Secret: "test"})
|
||||
}
|
||||
err := e.processSpotBalances(ctx, tc.input)
|
||||
if tc.err != nil {
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
} else {
|
||||
require.NoError(t, err, "processSpotBalances must not error")
|
||||
checkAccountChange(ctx, t, e, &tc)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBalancePushData(t *testing.T) {
|
||||
func TestProcessBalancePushData(t *testing.T) { //nolint:tparallel // Sequential tests, do not use t.Parallel(); Some timestamps are deliberately identical from trading activity
|
||||
t.Parallel()
|
||||
e := new(Exchange) //nolint:govet // Intentional shadow
|
||||
e.SetDefaults()
|
||||
e.Name = "ProcessFuturesBalancesTest"
|
||||
e.Accounts = accounts.MustNewAccounts(e)
|
||||
|
||||
// Sequential tests, do not use t.Run(); Some timestamps are deliberately identical from trading activity
|
||||
for _, tc := range []websocketBalancesTest{
|
||||
usdtLower := currency.USDT.Lower()
|
||||
|
||||
for i, tc := range []websocketBalancesTest{
|
||||
{
|
||||
input: []byte(`[{"timestamp":"1755718222"}]`),
|
||||
err: exchange.ErrCredentialsAreEmpty,
|
||||
@@ -123,16 +135,18 @@ func TestProcessBalancePushData(t *testing.T) {
|
||||
{
|
||||
deployCreds: true,
|
||||
input: []byte(`[{"balance":2214.191673190433,"change":-0.0025776,"currency":"usdt","text":"TCOM_USDT:263179103241933596","time":1755738515,"time_ms":1755738515671,"type":"fee","user":"12870774"}]`),
|
||||
expected: []account.Change{
|
||||
expected: accounts.SubAccounts{
|
||||
{
|
||||
Account: "12870774",
|
||||
ID: "12870774",
|
||||
AssetType: asset.USDTMarginedFutures,
|
||||
Balance: &account.Balance{
|
||||
Currency: currency.USDT,
|
||||
Total: 2214.191673190433,
|
||||
Free: 2214.191673190433,
|
||||
AvailableWithoutBorrow: 2214.191673190433,
|
||||
UpdatedAt: time.UnixMilli(1755738515671),
|
||||
Balances: accounts.CurrencyBalances{
|
||||
usdtLower: accounts.Balance{
|
||||
Currency: usdtLower,
|
||||
Total: 2214.191673190433,
|
||||
Free: 2214.191673190433,
|
||||
AvailableWithoutBorrow: 2214.191673190433,
|
||||
UpdatedAt: time.UnixMilli(1755738515671),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -140,33 +154,37 @@ func TestProcessBalancePushData(t *testing.T) {
|
||||
{
|
||||
deployCreds: true,
|
||||
input: []byte(`[{"balance":2214.189114310433,"change":-0.00255888,"currency":"usdt","text":"TCOM_USDT:263179103241933644","time":1755738516,"time_ms":1755738516430,"type":"fee","user":"12870774"}]`),
|
||||
expected: []account.Change{
|
||||
expected: accounts.SubAccounts{
|
||||
{
|
||||
Account: "12870774",
|
||||
ID: "12870774",
|
||||
AssetType: asset.USDTMarginedFutures,
|
||||
Balance: &account.Balance{
|
||||
Currency: currency.USDT,
|
||||
Total: 2214.189114310433,
|
||||
Free: 2214.189114310433,
|
||||
AvailableWithoutBorrow: 2214.189114310433,
|
||||
UpdatedAt: time.UnixMilli(1755738516430),
|
||||
Balances: accounts.CurrencyBalances{
|
||||
usdtLower: accounts.Balance{
|
||||
Currency: usdtLower,
|
||||
Total: 2214.189114310433,
|
||||
Free: 2214.189114310433,
|
||||
AvailableWithoutBorrow: 2214.189114310433,
|
||||
UpdatedAt: time.UnixMilli(1755738516430),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
ctx := t.Context()
|
||||
if tc.deployCreds {
|
||||
ctx = account.DeployCredentialsToContext(ctx, &account.Credentials{Key: "test", Secret: "test"})
|
||||
}
|
||||
err := e.processBalancePushData(ctx, tc.input, asset.USDTMarginedFutures)
|
||||
if tc.err != nil {
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err, "processBalancePushData must not error")
|
||||
require.Len(t, e.Websocket.DataHandler, 1)
|
||||
checkAccountChange(ctx, t, e, &tc)
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
// Sequential tests, do not use t.Parallel(); Some timestamps are deliberately identical from trading activity
|
||||
ctx := t.Context()
|
||||
if tc.deployCreds {
|
||||
ctx = accounts.DeployCredentialsToContext(ctx, &accounts.Credentials{Key: "test", Secret: "test"})
|
||||
}
|
||||
err := e.processBalancePushData(ctx, tc.input, asset.USDTMarginedFutures)
|
||||
if tc.err != nil {
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
} else {
|
||||
require.NoError(t, err, "processBalancePushData must not error")
|
||||
checkAccountChange(ctx, t, e, &tc)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,25 +193,19 @@ func checkAccountChange(ctx context.Context, t *testing.T, exch *Exchange, tc *w
|
||||
|
||||
require.Len(t, exch.Websocket.DataHandler, 1)
|
||||
payload := <-exch.Websocket.DataHandler
|
||||
received, ok := payload.([]account.Change)
|
||||
received, ok := payload.(accounts.SubAccounts)
|
||||
require.Truef(t, ok, "Expected account changes, got %T", payload)
|
||||
|
||||
require.Lenf(t, received, len(tc.expected), "Expected %d changes, got %d", len(tc.expected), len(received))
|
||||
for i, change := range received {
|
||||
assert.Equal(t, tc.expected[i].Account, change.Account, "account should equal")
|
||||
assert.Equal(t, tc.expected[i].AssetType, change.AssetType, "asset type should equal")
|
||||
assert.True(t, tc.expected[i].Balance.Currency.Equal(change.Balance.Currency), "currency should equal")
|
||||
assert.Equal(t, tc.expected[i].Balance.Total, change.Balance.Total, "total should equal")
|
||||
assert.Equal(t, tc.expected[i].Balance.Hold, change.Balance.Hold, "hold should equal")
|
||||
assert.Equal(t, tc.expected[i].Balance.Free, change.Balance.Free, "free should equal")
|
||||
assert.Equal(t, tc.expected[i].Balance.AvailableWithoutBorrow, change.Balance.AvailableWithoutBorrow, "available without borrow should equal")
|
||||
assert.Equal(t, tc.expected[i].Balance.Borrowed, change.Balance.Borrowed, "borrowed should equal")
|
||||
assert.Equal(t, tc.expected[i].Balance.UpdatedAt, change.Balance.UpdatedAt, "updated at should equal")
|
||||
require.Equal(t, tc.expected, received)
|
||||
|
||||
creds, err := exch.GetCredentials(ctx)
|
||||
require.NoError(t, err, "GetCredentials must not error")
|
||||
stored, err := account.GetBalance(exch.Name, tc.expected[i].Account, creds, tc.expected[i].AssetType, tc.expected[i].Balance.Currency)
|
||||
creds, err := exch.GetCredentials(ctx)
|
||||
require.NoError(t, err, "GetCredentials must not error")
|
||||
|
||||
for _, change := range received {
|
||||
bal := slices.Collect(maps.Values(change.Balances))[0]
|
||||
stored, err := exch.Accounts.GetBalance(change.ID, creds, change.AssetType, bal.Currency)
|
||||
require.NoError(t, err, "GetBalance must not error")
|
||||
assert.Equal(t, tc.expected[i].Balance.Free, stored.GetFree(), "free balance should equal with accounts stored value")
|
||||
assert.Equal(t, bal.Free, stored.Free, "free balance should equal with accounts stored value")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
@@ -675,56 +675,43 @@ func (e *Exchange) UpdateOrderbookWithLimit(ctx context.Context, p currency.Pair
|
||||
return orderbook.Get(e.Name, p, a)
|
||||
}
|
||||
|
||||
// UpdateAccountInfo retrieves balances for all enabled currencies for the
|
||||
func (e *Exchange) UpdateAccountInfo(ctx context.Context, a asset.Item) (account.Holdings, error) {
|
||||
info := account.Holdings{
|
||||
Exchange: e.Name,
|
||||
Accounts: []account.SubAccount{{
|
||||
AssetType: a,
|
||||
}},
|
||||
}
|
||||
// UpdateAccountBalances retrieves currency balances
|
||||
func (e *Exchange) UpdateAccountBalances(ctx context.Context, a asset.Item) (accounts.SubAccounts, error) {
|
||||
subAccts := accounts.SubAccounts{accounts.NewSubAccount(a, "")}
|
||||
switch a {
|
||||
case asset.Spot:
|
||||
balances, err := e.GetSpotAccounts(ctx, currency.EMPTYCODE)
|
||||
if err != nil {
|
||||
return info, err
|
||||
return nil, err
|
||||
}
|
||||
currencies := make([]account.Balance, len(balances))
|
||||
for i := range balances {
|
||||
currencies[i] = account.Balance{
|
||||
Currency: currency.NewCode(balances[i].Currency),
|
||||
Total: balances[i].Available.Float64() + balances[i].Locked.Float64(),
|
||||
Hold: balances[i].Locked.Float64(),
|
||||
Free: balances[i].Available.Float64(),
|
||||
}
|
||||
subAccts[0].Balances.Set(balances[i].Currency, accounts.Balance{
|
||||
Total: balances[i].Available.Float64() + balances[i].Locked.Float64(),
|
||||
Hold: balances[i].Locked.Float64(),
|
||||
Free: balances[i].Available.Float64(),
|
||||
})
|
||||
}
|
||||
info.Accounts[0].Currencies = currencies
|
||||
case asset.Margin, asset.CrossMargin:
|
||||
balances, err := e.GetMarginAccountList(ctx, currency.EMPTYPAIR)
|
||||
if err != nil {
|
||||
return info, err
|
||||
return nil, err
|
||||
}
|
||||
currencies := make([]account.Balance, 0, 2*len(balances))
|
||||
for i := range balances {
|
||||
currencies = append(currencies,
|
||||
account.Balance{
|
||||
Currency: currency.NewCode(balances[i].Base.Currency),
|
||||
Total: balances[i].Base.Available.Float64() + balances[i].Base.LockedAmount.Float64(),
|
||||
Hold: balances[i].Base.LockedAmount.Float64(),
|
||||
Free: balances[i].Base.Available.Float64(),
|
||||
},
|
||||
account.Balance{
|
||||
Currency: currency.NewCode(balances[i].Quote.Currency),
|
||||
Total: balances[i].Quote.Available.Float64() + balances[i].Quote.LockedAmount.Float64(),
|
||||
Hold: balances[i].Quote.LockedAmount.Float64(),
|
||||
Free: balances[i].Quote.Available.Float64(),
|
||||
})
|
||||
subAccts[0].Balances.Set(balances[i].Base.Currency, accounts.Balance{
|
||||
Total: balances[i].Base.Available.Float64() + balances[i].Base.LockedAmount.Float64(),
|
||||
Hold: balances[i].Base.LockedAmount.Float64(),
|
||||
Free: balances[i].Base.Available.Float64(),
|
||||
})
|
||||
subAccts[0].Balances.Set(balances[i].Quote.Currency, accounts.Balance{
|
||||
Total: balances[i].Quote.Available.Float64() + balances[i].Quote.LockedAmount.Float64(),
|
||||
Hold: balances[i].Quote.LockedAmount.Float64(),
|
||||
Free: balances[i].Quote.Available.Float64(),
|
||||
})
|
||||
}
|
||||
info.Accounts[0].Currencies = currencies
|
||||
case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.DeliveryFutures:
|
||||
settle, err := getSettlementCurrency(currency.EMPTYPAIR, a)
|
||||
if err != nil {
|
||||
return info, err
|
||||
return nil, err
|
||||
}
|
||||
var acc *FuturesAccount
|
||||
if a == asset.DeliveryFutures {
|
||||
@@ -733,33 +720,27 @@ func (e *Exchange) UpdateAccountInfo(ctx context.Context, a asset.Item) (account
|
||||
acc, err = e.QueryFuturesAccount(ctx, settle)
|
||||
}
|
||||
if err != nil {
|
||||
return info, err
|
||||
return nil, err
|
||||
}
|
||||
info.Accounts[0].Currencies = []account.Balance{{
|
||||
Currency: currency.NewCode(acc.Currency),
|
||||
Total: acc.Total.Float64(),
|
||||
Hold: acc.Total.Float64() - acc.Available.Float64(),
|
||||
Free: acc.Available.Float64(),
|
||||
}}
|
||||
subAccts[0].Balances.Set(acc.Currency, accounts.Balance{
|
||||
Total: acc.Total.Float64(),
|
||||
Hold: acc.Total.Float64() - acc.Available.Float64(),
|
||||
Free: acc.Available.Float64(),
|
||||
})
|
||||
case asset.Options:
|
||||
balance, err := e.GetOptionAccounts(ctx)
|
||||
if err != nil {
|
||||
return info, err
|
||||
return nil, err
|
||||
}
|
||||
info.Accounts[0].Currencies = []account.Balance{{
|
||||
Currency: currency.NewCode(balance.Currency),
|
||||
Total: balance.Total.Float64(),
|
||||
Hold: balance.Total.Float64() - balance.Available.Float64(),
|
||||
Free: balance.Available.Float64(),
|
||||
}}
|
||||
subAccts[0].Balances.Set(balance.Currency, accounts.Balance{
|
||||
Total: balance.Total.Float64(),
|
||||
Hold: balance.Total.Float64() - balance.Available.Float64(),
|
||||
Free: balance.Available.Float64(),
|
||||
})
|
||||
default:
|
||||
return info, fmt.Errorf("%w asset type: %v", asset.ErrNotSupported, a)
|
||||
return nil, fmt.Errorf("%w asset type: %q", asset.ErrNotSupported, a)
|
||||
}
|
||||
creds, err := e.GetCredentials(ctx)
|
||||
if err == nil {
|
||||
err = account.Process(&info, creds)
|
||||
}
|
||||
return info, err
|
||||
return subAccts, e.Accounts.Save(ctx, subAccts, true)
|
||||
}
|
||||
|
||||
// GetAccountFundingHistory returns funding history, deposits and
|
||||
@@ -1756,10 +1737,9 @@ func (e *Exchange) GetAvailableTransferChains(ctx context.Context, cryptocurrenc
|
||||
return availableChains, 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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user