diff --git a/pkg/exchange/okex/convert.go b/pkg/exchange/okex/convert.go index 15e800439..0751bf091 100644 --- a/pkg/exchange/okex/convert.go +++ b/pkg/exchange/okex/convert.go @@ -40,11 +40,15 @@ func toGlobalTicker(marketTicker okexapi.MarketTicker) *types.Ticker { func toGlobalBalance(account *okexapi.Account) types.BalanceMap { var balanceMap = types.BalanceMap{} - for _, balanceDetail := range account.Details { - balanceMap[balanceDetail.Currency] = types.Balance{ - Currency: balanceDetail.Currency, - Available: balanceDetail.CashBalance, - Locked: balanceDetail.Frozen, + for _, detail := range account.Details { + + balanceMap[detail.Currency] = types.Balance{ + Currency: detail.Currency, + Available: detail.Available, + Locked: detail.FrozenBalance, + Interest: detail.Interest, // accrued interest + Borrowed: detail.Liability.Abs(), // okx liability does not include the accrued interest + NetAsset: detail.Equity, } } return balanceMap diff --git a/pkg/exchange/okex/exchange.go b/pkg/exchange/okex/exchange.go index a7e5b614d..62d59998b 100644 --- a/pkg/exchange/okex/exchange.go +++ b/pkg/exchange/okex/exchange.go @@ -210,22 +210,28 @@ func (e *Exchange) PlatformFeeCurrency() string { } func (e *Exchange) QueryAccount(ctx context.Context) (*types.Account, error) { - bals, err := e.QueryAccountBalances(ctx) + accounts, err := e.queryAccountBalance(ctx) if err != nil { return nil, err } + if len(accounts) == 0 { + return nil, fmt.Errorf("account balance is empty") + } + + balances := toGlobalBalance(&accounts[0]) account := types.NewAccount() - account.UpdateBalances(bals) + account.UpdateBalances(balances) + + // for margin account + account.MarginRatio = accounts[0].MarginRatio + account.TotalAccountValue = accounts[0].TotalEquityInUSD + return account, nil } func (e *Exchange) QueryAccountBalances(ctx context.Context) (types.BalanceMap, error) { - if err := queryAccountLimiter.Wait(ctx); err != nil { - return nil, fmt.Errorf("account rate limiter wait error: %w", err) - } - - accountBalances, err := e.client.NewGetAccountBalanceRequest().Do(ctx) + accountBalances, err := e.queryAccountBalance(ctx) if err != nil { return nil, err } @@ -237,6 +243,14 @@ func (e *Exchange) QueryAccountBalances(ctx context.Context) (types.BalanceMap, return toGlobalBalance(&accountBalances[0]), nil } +func (e *Exchange) queryAccountBalance(ctx context.Context) ([]okexapi.Account, error) { + if err := queryAccountLimiter.Wait(ctx); err != nil { + return nil, fmt.Errorf("account rate limiter wait error: %w", err) + } + + return e.client.NewGetAccountBalanceRequest().Do(ctx) +} + func (e *Exchange) SubmitOrder(ctx context.Context, order types.SubmitOrder) (*types.Order, error) { orderReq := e.client.NewPlaceOrderRequest() diff --git a/pkg/exchange/okex/okexapi/client_test.go b/pkg/exchange/okex/okexapi/client_test.go index c944a4fbe..16cf3b25e 100644 --- a/pkg/exchange/okex/okexapi/client_test.go +++ b/pkg/exchange/okex/okexapi/client_test.go @@ -66,15 +66,17 @@ func TestClient_GetMarketTicker(t *testing.T) { t.Logf("tickers: %+v", tickers) } -func TestClient_GetAcountInfo(t *testing.T) { +func TestClient_GetAccountBalance(t *testing.T) { client := getTestClientOrSkip(t) ctx := context.Background() req := client.NewGetAccountBalanceRequest() - acct, err := req.Do(ctx) - assert.NoError(t, err) - assert.NotEmpty(t, acct) - t.Logf("acct: %+v", acct) + resp, err := req.Do(ctx) + if assert.NoError(t, err) { + assert.NotEmpty(t, resp) + t.Logf("account balance: %+v", resp[0]) + debugJson(t, resp[0]) + } } func TestClient_GetFundingRateRequest(t *testing.T) { @@ -463,10 +465,15 @@ func TestClient_Margin(t *testing.T) { t.Logf("positions: %+v", resp) if len(resp) > 0 { - out, err2 := json.MarshalIndent(resp[0], "", " ") - assert.NoError(t, err2) - t.Logf("positions: %s", out) + debugJson(t, resp[0]) } } }) } + +func debugJson(t *testing.T, a any) { + out, err := json.MarshalIndent(a, "", " ") + if assert.NoError(t, err) { + t.Log(string(out)) + } +} diff --git a/pkg/exchange/okex/okexapi/get_account_balance_request.go b/pkg/exchange/okex/okexapi/get_account_balance_request.go index 74b8a4b7f..688a2434b 100644 --- a/pkg/exchange/okex/okexapi/get_account_balance_request.go +++ b/pkg/exchange/okex/okexapi/get_account_balance_request.go @@ -11,20 +11,62 @@ import ( //go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data type BalanceDetail struct { - Currency string `json:"ccy"` - Available fixedpoint.Value `json:"availEq"` - CashBalance fixedpoint.Value `json:"cashBal"` + Available fixedpoint.Value `json:"availBal"` + AvailEquity fixedpoint.Value `json:"availEq"` + + // BorrowFrozen is Potential borrowing IMR of the account in USD + BorrowFrozen fixedpoint.Value `json:"borrowFroz"` + CashBalance fixedpoint.Value `json:"cashBal"` + Currency string `json:"ccy"` + CrossLiab fixedpoint.Value `json:"crossLiab,omitempty"` + DisEquity fixedpoint.Value `json:"disEq,omitempty"` + Equity fixedpoint.Value `json:"eq,omitempty"` + EquityInUSD fixedpoint.Value `json:"eqUsd"` + SmtSyncEquity fixedpoint.Value `json:"smtSyncEq,omitempty"` + SpotCopyTradingEq fixedpoint.Value `json:"spotCopyTradingEq,omitempty"` + + FixedBalance fixedpoint.Value `json:"fixedBal"` + FrozenBalance fixedpoint.Value `json:"frozenBal"` + + Imr string `json:"imr,omitempty"` + Interest fixedpoint.Value `json:"interest"` + IsoEquity fixedpoint.Value `json:"isoEq,omitempty"` + IsoLiability fixedpoint.Value `json:"isoLiab,omitempty"` + IsoUpl fixedpoint.Value `json:"isoUpl,omitempty"` + Liability fixedpoint.Value `json:"liab,omitempty"` + MaxLoan fixedpoint.Value `json:"maxLoan,omitempty"` + MgnRatio fixedpoint.Value `json:"mgnRatio,omitempty"` + Mmr string `json:"mmr,omitempty"` + NotionalLever string `json:"notionalLever"` + + // OrderFrozen is margin frozen for open orders OrderFrozen fixedpoint.Value `json:"ordFrozen"` - Frozen fixedpoint.Value `json:"frozenBal"` - Equity fixedpoint.Value `json:"eq"` - EquityInUSD fixedpoint.Value `json:"eqUsd"` + RewardBal fixedpoint.Value `json:"rewardBal,omitempty"` + SpotInUseAmt fixedpoint.Value `json:"spotInUseAmt,omitempty"` + ClSpotInUseAmt fixedpoint.Value `json:"clSpotInUseAmt,omitempty"` + MaxSpotInUse fixedpoint.Value `json:"maxSpotInUse,omitempty"` + SpotIsoBal fixedpoint.Value `json:"spotIsoBal,omitempty"` + StgyEquity fixedpoint.Value `json:"stgyEq"` + Twap string `json:"twap,omitempty"` UpdateTime types.MillisecondTimestamp `json:"uTime"` UnrealizedProfitAndLoss fixedpoint.Value `json:"upl"` + UplLiab fixedpoint.Value `json:"uplLiab"` + SpotBal fixedpoint.Value `json:"spotBal"` + OpenAvgPx fixedpoint.Value `json:"openAvgPx,omitempty"` + AccAvgPx fixedpoint.Value `json:"accAvgPx,omitempty"` + SpotUpl fixedpoint.Value `json:"spotUpl,omitempty"` + SpotUplRatio fixedpoint.Value `json:"spotUplRatio,omitempty"` + TotalPnl fixedpoint.Value `json:"totalPnl,omitempty"` + TotalPnlRatio fixedpoint.Value `json:"totalPnlRatio,omitempty"` } type Account struct { - TotalEquityInUSD fixedpoint.Value `json:"totalEq"` - UpdateTime types.MillisecondTimestamp `json:"uTime"` + TotalEquityInUSD fixedpoint.Value `json:"totalEq,omitempty"` + AdjustEquity fixedpoint.Value `json:"adjEq,omitempty"` + UpdateTime types.MillisecondTimestamp `json:"uTime,omitempty"` + MarginRatio fixedpoint.Value `json:"mgnRatio,omitempty"` + NotionalUsd fixedpoint.Value `json:"notionalUsd,omitempty"` + UnrealizedPnl fixedpoint.Value `json:"upl,omitempty"` Details []BalanceDetail `json:"details"` }