bybit: Fix UpdateAccountInfo free and hold calculations (#1980)

* fix free and hold calculations

* rm verbosity

* gk/glorious: nit truncate loaded value from http mock recording to adhere to test

* gk: readability nit

* fix test issue

* regig calculation and confirmed upstream

* thrasher: nits

* update tests

---------

Co-authored-by: shazbert <ryan.oharareid@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2025-09-15 16:05:52 +10:00
committed by GitHub
parent 17940f800b
commit c153716e89
3 changed files with 34 additions and 24 deletions

View File

@@ -1636,7 +1636,7 @@ func TestGetWalletBalance(t *testing.T) {
assert.Equal(t, types.Number(0), r.List[0].Coin[x].AvailableToBorrow, "AvailableToBorrow should be correct")
assert.Equal(t, types.Number(0), r.List[0].Coin[x].AvailableToWithdraw, "AvailableToWithdraw should be correct")
assert.Equal(t, types.Number(0), r.List[0].Coin[x].Bonus, "Bonus should be correct")
assert.Equal(t, types.Number(30723.630216383711792744), r.List[0].Coin[x].BorrowAmount, "BorrowAmount should be correct")
assert.Equal(t, types.Number(30723.630216383714), r.List[0].Coin[x].BorrowAmount, "BorrowAmount should be correct")
assert.Equal(t, currency.USDC, r.List[0].Coin[x].Coin, "Coin should be correct")
assert.True(t, r.List[0].Coin[x].CollateralSwitch, "CollateralSwitch should match")
assert.Equal(t, types.Number(0), r.List[0].Coin[x].CumulativeRealisedPNL, "CumulativeRealisedPNL should be correct")
@@ -2879,21 +2879,24 @@ func TestUpdateAccountInfo(t *testing.T) {
case 0:
assert.Equal(t, currency.USDC, r.Accounts[0].Currencies[x].Currency, "Currency should be USDC")
assert.Equal(t, -30723.63021638, r.Accounts[0].Currencies[x].Total, "Total amount should be correct")
assert.Equal(t, -30723.63021638, r.Accounts[0].Currencies[x].Hold, "Hold amount should be correct")
assert.Equal(t, 30723.630216383714, r.Accounts[0].Currencies[x].Borrowed, "Borrowed amount should be correct")
assert.Equal(t, 0.0, r.Accounts[0].Currencies[x].Free, "Free amount should be correct")
assert.Zero(t, r.Accounts[0].Currencies[x].Hold, "Hold amount should be zero")
assert.Equal(t, 30723.630216383711792744, r.Accounts[0].Currencies[x].Borrowed, "Borrowed amount should be correct")
assert.Zero(t, r.Accounts[0].Currencies[x].Free, "Free amount should be zero")
assert.Zero(t, r.Accounts[0].Currencies[x].AvailableWithoutBorrow, "AvailableWithoutBorrow amount should be zero")
case 1:
assert.Equal(t, currency.AVAX, r.Accounts[0].Currencies[x].Currency, "Currency should be AVAX")
assert.Equal(t, 2473.9, r.Accounts[0].Currencies[x].Total, "Total amount should be correct")
assert.Equal(t, 1468.10808813, r.Accounts[0].Currencies[x].Hold, "Hold amount should be correct")
assert.Equal(t, 0.0, r.Accounts[0].Currencies[x].Borrowed, "Borrowed amount should be correct")
assert.Equal(t, 1005.79191187, r.Accounts[0].Currencies[x].Free, "Free amount should be correct")
assert.Zero(t, r.Accounts[0].Currencies[x].Hold, "Hold amount should be zero")
assert.Zero(t, r.Accounts[0].Currencies[x].Borrowed, "Borrowed amount should be zero")
assert.Equal(t, 2473.9, r.Accounts[0].Currencies[x].Free, "Free amount should be correct")
assert.Equal(t, 1005.79191187, r.Accounts[0].Currencies[x].AvailableWithoutBorrow, "AvailableWithoutBorrow amount should be correct")
case 2:
assert.Equal(t, currency.USDT, r.Accounts[0].Currencies[x].Currency, "Currency should be USDT")
assert.Equal(t, 935.1415, r.Accounts[0].Currencies[x].Total, "Total amount should be correct")
assert.Equal(t, 0.0, r.Accounts[0].Currencies[x].Borrowed, "Borrowed amount should be correct")
assert.Equal(t, 0.0, r.Accounts[0].Currencies[x].Hold, "Hold amount should be correct")
assert.Zero(t, r.Accounts[0].Currencies[x].Borrowed, "Borrowed amount should be zero")
assert.Zero(t, r.Accounts[0].Currencies[x].Hold, "Hold amount should be zero")
assert.Equal(t, 935.1415, r.Accounts[0].Currencies[x].Free, "Free amount should be correct")
assert.Equal(t, 935.1415, r.Accounts[0].Currencies[x].AvailableWithoutBorrow, "AvailableWithoutBorrow amount should be correct")
}
}
}

View File

@@ -895,7 +895,7 @@ type WalletBalance struct {
TotalWalletBalance types.Number `json:"totalWalletBalance"`
AccountLTV types.Number `json:"accountLTV"` // Account LTV: account total borrowed size / (account total equity + account total borrowed size).
TotalMaintenanceMargin types.Number `json:"totalMaintenanceMargin"`
Coin []struct {
Coin []*struct {
AvailableToBorrow types.Number `json:"availableToBorrow"`
Bonus types.Number `json:"bonus"`
AccruedInterest types.Number `json:"accruedInterest"`

View File

@@ -656,9 +656,7 @@ func (e *Exchange) UpdateAccountInfo(ctx context.Context, assetType asset.Item)
return info, err
}
switch assetType {
case asset.Spot, asset.Options,
asset.USDCMarginedFutures,
asset.USDTMarginedFutures:
case asset.Spot, asset.Options, asset.USDCMarginedFutures, asset.USDTMarginedFutures:
switch at {
case accountTypeUnified:
accountType = "UNIFIED"
@@ -680,18 +678,27 @@ func (e *Exchange) UpdateAccountInfo(ctx context.Context, assetType asset.Item)
}
currencyBalance := []account.Balance{}
for i := range balances.List {
for c := range balances.List[i].Coin {
balance := account.Balance{
Currency: balances.List[i].Coin[c].Coin,
Total: balances.List[i].Coin[c].WalletBalance.Float64(),
Free: balances.List[i].Coin[c].AvailableToWithdraw.Float64(),
Borrowed: balances.List[i].Coin[c].BorrowAmount.Float64(),
Hold: balances.List[i].Coin[c].WalletBalance.Float64() - balances.List[i].Coin[c].AvailableToWithdraw.Float64(),
for _, c := range balances.List[i].Coin {
// borrow amounts get truncated to 8 dec places when total and equity are calculated on the exchange
truncBorrow := c.BorrowAmount.Decimal().Truncate(8).InexactFloat64()
// wallet balance can be negative when borrow is present, and wallet balance will be offset with spot holdings
// e.g. borrow $10,000, wallet balance will be -$9,900 ∴ spot holding $100
balanceDiff := truncBorrow + c.WalletBalance.Float64()
freeBalance := balanceDiff - c.Locked.Float64()
if assetType == asset.Spot && c.AvailableBalanceForSpot.Float64() != 0 {
freeBalance = c.AvailableBalanceForSpot.Float64()
}
if assetType == asset.Spot && balances.List[i].Coin[c].AvailableBalanceForSpot.Float64() != 0 {
balance.Free = balances.List[i].Coin[c].AvailableBalanceForSpot.Float64()
}
currencyBalance = append(currencyBalance, balance)
currencyBalance = append(currencyBalance, account.Balance{
Currency: c.Coin,
Total: c.WalletBalance.Float64(),
Free: freeBalance,
Borrowed: c.BorrowAmount.Float64(),
Hold: c.Locked.Float64(),
AvailableWithoutBorrow: c.AvailableToWithdraw.Float64(),
})
}
}
acc.Currencies = currencyBalance