Skip to content

Commit

Permalink
feat(v5): Add /v5/account/wallet-balance and /v5/user/query-api e…
Browse files Browse the repository at this point in the history
…ndpoints (#80)

* feat(v5): add `/v5/account/wallet-balance` endpoint

* chore: comment

* chore: move enums to enum file

* chore: fix comment

* feat(v5): add intf for User category

* docs(v5): Add `/v5/user/query-api`

* feat(v5): add support for `/v5/user/query-api`

* fix: wrong struct

* test: add user servce tests

* test: add integration
  • Loading branch information
rtunazzz authored Feb 19, 2023
1 parent 804dbfe commit 9657351
Show file tree
Hide file tree
Showing 11 changed files with 365 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ The following API endpoints have been implemented

- [`/v5/order/create` Place Order](https://bybit-exchange.github.io/docs/v5/order/create-order)

#### Account

- [`/v5/account/wallet-balance` Get Wallet Balance](https://bybit-exchange.github.io/docs/v5/account/wallet-balance)

#### User

- [`/v5/user/query-api` Get API Key Information](https://bybit-exchange.github.io/docs/v5/user/apikey-info)


### REST API

#### [Derivatives Unified Margin](https://bybit-exchange.github.io/docs/derivativesV3/unified_margin)
Expand Down
8 changes: 8 additions & 0 deletions integrationtest/v5/account/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BYBIT_TEST_UPDATED=false

test:
BYBIT_TEST_UPDATED=${BYBIT_TEST_UPDATED} BYBIT_TEST_KEY=${BYBIT_TEST_KEY} BYBIT_TEST_SECRET=${BYBIT_TEST_SECRET} go test github.com/hirokisan/bybit/v2/integrationtest/v5/account -v -tags=integrationtestv5account

test-spec:
BYBIT_TEST_UPDATED=${BYBIT_TEST_UPDATED} BYBIT_TEST_KEY=${BYBIT_TEST_KEY} BYBIT_TEST_SECRET=${BYBIT_TEST_SECRET} go test github.com/hirokisan/bybit/v2/integrationtest/v5/account -v -tags=integrationtestv5account -run ${BYBIT_TEST_METHOD}

23 changes: 23 additions & 0 deletions integrationtest/v5/account/wallet_balance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//go:build integrationtestv5account

package integrationtestv5account

import (
"testing"

"github.com/hirokisan/bybit/v2"
"github.com/hirokisan/bybit/v2/integrationtest/testhelper"
"github.com/stretchr/testify/require"
)

func TestGetWalletBalance(t *testing.T) {
client := bybit.NewTestClient().WithAuthFromEnv()
symbol := bybit.SymbolV5BTCUSDT
res, err := client.V5().Account().GetWalletBalance(bybit.UnifiedAccount, "")
require.NoError(t, err)
{
goldenFilename := "./testdata/v5-account-get-wallet-balance.json" // TODO
testhelper.Compare(t, goldenFilename, testhelper.ConvertToJSON(res.Result))
testhelper.UpdateFile(t, goldenFilename, testhelper.ConvertToJSON(res.Result))
}
}
8 changes: 8 additions & 0 deletions integrationtest/v5/user/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BYBIT_TEST_UPDATED=false

test:
BYBIT_TEST_UPDATED=${BYBIT_TEST_UPDATED} BYBIT_TEST_KEY=${BYBIT_TEST_KEY} BYBIT_TEST_SECRET=${BYBIT_TEST_SECRET} go test github.com/hirokisan/bybit/v2/integrationtest/v5/user -v -tags=integrationtestv5user

test-spec:
BYBIT_TEST_UPDATED=${BYBIT_TEST_UPDATED} BYBIT_TEST_KEY=${BYBIT_TEST_KEY} BYBIT_TEST_SECRET=${BYBIT_TEST_SECRET} go test github.com/hirokisan/bybit/v2/integrationtest/v5/user -v -tags=integrationtestv5user -run ${BYBIT_TEST_METHOD}

22 changes: 22 additions & 0 deletions integrationtest/v5/user/api_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build integrationtestv5user

package integrationtestv5user

import (
"testing"

"github.com/hirokisan/bybit/v2"
"github.com/hirokisan/bybit/v2/integrationtest/testhelper"
"github.com/stretchr/testify/require"
)

func TestGetPositionInfo(t *testing.T) {
client := bybit.NewTestClient().WithAuthFromEnv()
res, err := client.V5().User().GetAPIKeyInfo()
require.NoError(t, err)
{
goldenFilename := "./testdata/v5-user-get-api-key-info.json" // TODO
testhelper.Compare(t, goldenFilename, testhelper.ConvertToJSON(res.Result))
testhelper.UpdateFile(t, goldenFilename, testhelper.ConvertToJSON(res.Result))
}
}
73 changes: 73 additions & 0 deletions v5_account_service.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,83 @@
package bybit

import (
"net/url"
)

// V5AccountServiceI :
type V5AccountServiceI interface {
GetWalletBalance(AccountType, string) (*V5WalletBalanceResponse, error)
}

// V5AccountService :
type V5AccountService struct {
client *Client
}

// V5WalletBalanceResponse :
type V5WalletBalanceResponse struct {
CommonV5Response `json:",inline"`
Result V5WalletBalanceResult `json:"result"`
}

// V5WalletBalanceResult :
type V5WalletBalanceResult struct {
List []V5WalletBalanceList `json:"list"`
}

// VV5WalletBalanceCoin :
type V5WalletBalanceCoin struct {
AvailableToBorrow string `json:"availableToBorrow"`
AccruedInterest string `json:"accruedInterest"`
AvailableToWithdraw string `json:"availableToWithdraw"`
TotalOrderIM string `json:"totalOrderIM"`
Equity string `json:"equity"`
TotalPositionMM string `json:"totalPositionMM"`
UsdValue string `json:"usdValue"`
UnrealisedPnl string `json:"unrealisedPnl"`
BorrowAmount string `json:"borrowAmount"`
TotalPositionIM string `json:"totalPositionIM"`
WalletBalance string `json:"walletBalance"`
CumRealisedPnl string `json:"cumRealisedPnl"`
Coin string `json:"coin"`
}

// V5WalletBalanceList :
type V5WalletBalanceList struct {
TotalEquity string `json:"totalEquity"`
AccountIMRate string `json:"accountIMRate"`
TotalMarginBalance string `json:"totalMarginBalance"`
TotalInitialMargin string `json:"totalInitialMargin"`
AccountType string `json:"accountType"`
TotalAvailableBalance string `json:"totalAvailableBalance"`
AccountMMRate string `json:"accountMMRate"`
TotalPerpUPL string `json:"totalPerpUPL"`
TotalWalletBalance string `json:"totalWalletBalance"`
TotalMaintenanceMargin string `json:"totalMaintenanceMargin"`
Coin []V5WalletBalanceCoin `json:"coin"`
}

// GetWalletBalance :
//
// at: UNIFIED or CONTRACT
//
// coin:
// If not passed, it returns non-zero asset info
// You can pass multiple coins to query, separated by comma. "USDT,USDC".
func (s *V5AccountService) GetWalletBalance(at AccountType, coin string) (*V5WalletBalanceResponse, error) {
var (
res V5WalletBalanceResponse
query = make(url.Values)
)

query.Add("accountType", string(at))
if coin != "" {
query.Add("coin", coin)
}

if err := s.client.getV5Privately("/v5/account/wallet-balance", query, &res); err != nil {
return nil, err
}

return &res, nil
}
71 changes: 71 additions & 0 deletions v5_account_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package bybit

import (
"encoding/json"
"net/http"
"testing"

"github.com/hirokisan/bybit/v2/testhelper"
"github.com/stretchr/testify/require"
)

func TestV5Account_GetWalletBalance(t *testing.T) {
t.Run("success", func(t *testing.T) {
path := "/v5/account/wallet-balance"
method := http.MethodGet
status := http.StatusOK
respBody := map[string]interface{}{
"result": map[string]interface{}{
"list": []map[string]interface{}{
{
"totalEquity": "18070.32797922",
"accountIMRate": "0.0101",
"totalMarginBalance": "18070.32797922",
"totalInitialMargin": "182.60183684",
"accountType": "UNIFIED",
"totalAvailableBalance": "17887.72614237",
"accountMMRate": "0",
"totalPerpUPL": "-0.11001349",
"totalWalletBalance": "18070.43799271",
"totalMaintenanceMargin": "0.38106773",
"coin": []map[string]interface{}{
{
"availableToBorrow": "2.5",
"accruedInterest": "0",
"availableToWithdraw": "0.805994",
"totalOrderIM": "0",
"equity": "0.805994",
"totalPositionMM": "0",
"usdValue": "12920.95352538",
"unrealisedPnl": "0",
"borrowAmount": "0",
"totalPositionIM": "0",
"walletBalance": "0.805994",
"cumRealisedPnl": "0",
"coin": "BTC",
},
},
},
},
},
}

bytesBody, err := json.Marshal(respBody)
require.NoError(t, err)

server, teardown := testhelper.NewServer(
testhelper.WithHandlerOption(path, method, status, bytesBody),
)
defer teardown()

client := NewTestClient().
WithBaseURL(server.URL).
WithAuth("test", "test")

resp, err := client.V5().Account().GetWalletBalance(UnifiedAccount, "")
require.NoError(t, err)

require.NotNil(t, resp)
testhelper.Compare(t, respBody["result"], resp.Result)
})
}
6 changes: 6 additions & 0 deletions v5_client_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type V5ServiceI interface {
SpotLeverageToken() V5SpotLeverageTokenServiceI
SpotMarginTrade() V5SpotMarginTradeServiceI
Asset() V5AssetServiceI
User() V5UserServiceI
}

// V5Service :
Expand Down Expand Up @@ -57,6 +58,11 @@ func (s *V5Service) Asset() V5AssetServiceI {
return &V5AssetService{s.client}
}

// User :
func (s *V5Service) User() V5UserServiceI {
return &V5UserService{s.client}
}

// V5 :
func (c *Client) V5() V5ServiceI {
return &V5Service{c.withCheckResponseBody(checkV5ResponseBody)}
Expand Down
8 changes: 8 additions & 0 deletions v5_enum.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package bybit

// AccountType :
type AccountType string

const (
UnifiedAccount AccountType = "UNIFIED"
NormalAccount AccountType = "CONTRACT"
)

// CategoryV5 :
type CategoryV5 string

Expand Down
67 changes: 67 additions & 0 deletions v5_user_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package bybit

import (
"net/url"
"time"
)

// V5UserServiceI :
type V5UserServiceI interface {
GetAPIKey() (*V5APIKeyResponse, error)
}

// V5UserService :
type V5UserService struct {
client *Client
}

// V5APIKeyResponse :
type V5APIKeyResponse struct {
CommonV5Response `json:",inline"`
Result V5ApiKeyResult `json:"result"`
}

// V5ApiKeyResult :
type V5ApiKeyResult struct {
ID string `json:"id"`
Note string `json:"note"`
APIKey string `json:"apiKey"`
ReadOnly int `json:"readOnly"`
Secret string `json:"secret"`
Permissions struct {
ContractTrade []interface{} `json:"ContractTrade"`
Spot []interface{} `json:"Spot"`
Wallet []string `json:"Wallet"`
Options []interface{} `json:"Options"`
Derivatives []interface{} `json:"Derivatives"`
CopyTrading []interface{} `json:"CopyTrading"`
BlockTrade []interface{} `json:"BlockTrade"`
Exchange []interface{} `json:"Exchange"`
Nft []interface{} `json:"NFT"`
} `json:"permissions"`
Ips []string `json:"ips"`
Type int `json:"type"`
DeadlineDay int `json:"deadlineDay"`
ExpiredAt time.Time `json:"expiredAt"`
CreatedAt time.Time `json:"createdAt"`
Unified int `json:"unified"`
Uta int `json:"uta"`
UserID int `json:"userID"`
InviterID int `json:"inviterID"`
VipLevel string `json:"vipLevel"`
MktMakerLevel string `json:"mktMakerLevel"`
AffiliateID int `json:"affiliateID"`
}

// GetAPIKey :
func (s *V5UserService) GetAPIKey() (*V5APIKeyResponse, error) {
var (
res V5APIKeyResponse
)

if err := s.client.getV5Privately("/v5/user/query-api", url.Values{}, &res); err != nil {
return nil, err
}

return &res, nil
}
Loading

0 comments on commit 9657351

Please sign in to comment.