Skip to content

Commit

Permalink
feat: support a few bybit rest apis
Browse files Browse the repository at this point in the history
  • Loading branch information
linstohu committed Dec 21, 2023
1 parent f0ebeed commit fda67be
Show file tree
Hide file tree
Showing 8 changed files with 645 additions and 0 deletions.
136 changes: 136 additions & 0 deletions bybit/rest/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2023, LinstoHu
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package rest

import (
"context"
"net/http"

"github.com/linstohu/nexapi/bybit/rest/types"
"github.com/linstohu/nexapi/utils"
)

// GetUnifiedAccountBalance(Unified Account API): UNIFIED (trade spot/linear/options)
// Note: the Unified account supports inverse trading. However, the margin used is from the inverse derivatives wallet instead of the unified wallet.
// doc: https://bybit-exchange.github.io/docs/v5/intro#current-api-coverage
func (bb *BybitClient) GetUnifiedAccountBalance() (*types.GetWalletBalanceResp, error) {
req := utils.HTTPRequest{
BaseURL: bb.cli.GetBaseURL(),
Path: "/v5/account/wallet-balance",
Method: http.MethodGet,
Query: types.GetWalletBalanceParam{
AccountType: types.UNIFIED,
},
}

headers, err := bb.cli.GenAuthHeaders(req)
if err != nil {
return nil, err
}
req.Headers = headers

resp, err := bb.cli.SendHTTPRequest(context.TODO(), req)
if err != nil {
return nil, err
}

var body types.GetWalletBalanceAPIResp

if err := resp.ReadJsonBody(&body); err != nil {
return nil, err
}

data := &types.GetWalletBalanceResp{
Http: resp,
Body: &body,
}

return data, nil
}

// GetContractAccountBalance(Unified Account API): CONTRACT(trade inverse)
func (bb *BybitClient) GetUnifiedAccountContractBalance() (*types.GetWalletBalanceResp, error) {
req := utils.HTTPRequest{
BaseURL: bb.cli.GetBaseURL(),
Path: "/v5/account/wallet-balance",
Method: http.MethodGet,
Query: types.GetWalletBalanceParam{
AccountType: types.CONTRACT,
},
}

headers, err := bb.cli.GenAuthHeaders(req)
if err != nil {
return nil, err
}
req.Headers = headers

resp, err := bb.cli.SendHTTPRequest(context.TODO(), req)
if err != nil {
return nil, err
}

var body types.GetWalletBalanceAPIResp

if err := resp.ReadJsonBody(&body); err != nil {
return nil, err
}

data := &types.GetWalletBalanceResp{
Http: resp,
Body: &body,
}

return data, nil
}

// GetFundAccountBalance get Funding wallet balance
func (bb *BybitClient) GetFundAccountBalance() (*types.GetAccountBalanceResp, error) {
req := utils.HTTPRequest{
BaseURL: bb.cli.GetBaseURL(),
Path: "/v5/asset/transfer/query-account-coins-balance",
Method: http.MethodGet,
Query: types.GetAccountBalanceParam{
AccountType: types.FUND,
},
}

headers, err := bb.cli.GenAuthHeaders(req)
if err != nil {
return nil, err
}
req.Headers = headers

resp, err := bb.cli.SendHTTPRequest(context.TODO(), req)
if err != nil {
return nil, err
}

var body types.GetAccountBalanceAPIResp

if err := resp.ReadJsonBody(&body); err != nil {
return nil, err
}

data := &types.GetAccountBalanceResp{
Http: resp,
Body: &body,
}

return data, nil
}
49 changes: 49 additions & 0 deletions bybit/rest/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2023, LinstoHu
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package rest

import (
"github.com/go-playground/validator"
bybitutils "github.com/linstohu/nexapi/bybit/utils"
)

type BybitClient struct {
cli *bybitutils.BybitClient

// validate struct fields
validate *validator.Validate
}

func NewBybitClient(cfg *bybitutils.BybitClientCfg) (*BybitClient, error) {
validator := validator.New()

err := validator.Struct(cfg)
if err != nil {
return nil, err
}

cli, err := bybitutils.NewBybitClient(cfg)
if err != nil {
return nil, err
}

return &BybitClient{
cli: cli,
validate: validator,
}, nil
}
75 changes: 75 additions & 0 deletions bybit/rest/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2023, LinstoHu
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package rest

import (
"fmt"
"os"
"testing"

"github.com/linstohu/nexapi/bybit/utils"
"github.com/stretchr/testify/assert"
)

func testNewClient(t *testing.T) *BybitClient {
cli, err := NewBybitClient(&utils.BybitClientCfg{
Debug: true,
BaseURL: utils.TestBaseURL,
Key: os.Getenv("BYBIT_KEY"),
Secret: os.Getenv("BYBIT_SECRET"),
})

if err != nil {
t.Fatalf("Could not create bybit client, %s", err)
}

return cli
}

func TestGetUnifiedAccountBalance(t *testing.T) {
cli := testNewClient(t)

resp, err := cli.GetUnifiedAccountBalance()
assert.Nil(t, err)

for _, v := range resp.Body.Result.List {
fmt.Printf("%+v\n", v)
}
}

func TestGetUnifiedAccountContractBalance(t *testing.T) {
cli := testNewClient(t)

resp, err := cli.GetUnifiedAccountContractBalance()
assert.Nil(t, err)

for _, v := range resp.Body.Result.List {
fmt.Printf("%+v\n", v)
}
}

func TestGetFundAccountBalance(t *testing.T) {
cli := testNewClient(t)

resp, err := cli.GetFundAccountBalance()
assert.Nil(t, err)

for _, v := range resp.Body.Result.Balance {
fmt.Printf("%+v\n", v)
}
}
112 changes: 112 additions & 0 deletions bybit/rest/types/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2023, LinstoHu
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package types

import "github.com/linstohu/nexapi/utils"

type GetWalletBalanceParam struct {
AccountType AccountType `url:"accountType" validate:"required,oneof=UNIFIED SPOT OPTION CONTRACT FUND"` //
}

// AccountType
// doc: https://bybit-exchange.github.io/docs/v5/account/wallet-balance
// - Unified account: UNIFIED (trade spot/linear/options), CONTRACT(trade inverse)
// - Classic account: CONTRACT, SPOT
//
// AccountType Enum doc: https://bybit-exchange.github.io/docs/v5/enum#accounttype
type AccountType = string

const (
UNIFIED = "UNIFIED"
SPOT = "SPOT"
OPTION = "OPTION"
CONTRACT = "CONTRACT"
FUND = "FUND"
)

type GetWalletBalanceResp struct {
Http *utils.ApiResponse
Body *GetWalletBalanceAPIResp
}

type GetWalletBalanceAPIResp struct {
Response `json:",inline"`
Result WalletBalanceResult `json:"result"`
}

type WalletBalanceResult struct {
List []WalletBalanceList `json:"list"`
}

type WalletBalanceList 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 []WalletBalanceCoin `json:"coin"`
}

type WalletBalanceCoin 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"`
}

type GetAccountBalanceParam struct {
AccountType AccountType `url:"accountType" validate:"required,oneof=UNIFIED SPOT OPTION CONTRACT FUND"`
WithBonus string `url:"accountType,omitempty"`
}

type GetAccountBalanceResp struct {
Http *utils.ApiResponse
Body *GetAccountBalanceAPIResp
}

type GetAccountBalanceAPIResp struct {
Response `json:",inline"`
Result GetAccountBalanceResult `json:"result"`
}

type GetAccountBalanceResult struct {
MemberID string `json:"memberId"`
AccountType string `json:"accountType"`
Balance []AccountBalance `json:"balance"`
}

type AccountBalance struct {
Coin string `json:"coin"`
TransferBalance string `json:"transferBalance"`
WalletBalance string `json:"walletBalance"`
Bonus string `json:"bonus"`
}
Loading

0 comments on commit fda67be

Please sign in to comment.