diff --git a/bybit_api_client.go b/bybit_api_client.go index 5626b9f..5ea6c43 100644 --- a/bybit_api_client.go +++ b/bybit_api_client.go @@ -302,3 +302,18 @@ func (c *Client) NewLendingServiceNoParams() *LendingServiceClient { c: c, } } + +func (c *Client) NewSpotLeverageService(params map[string]interface{}) *SpotLeverageClient { + return &SpotLeverageClient{ + c: c, + params: params, + } +} + +func (c *Client) NewSpotMarginDataService(params map[string]interface{}, isUta bool) *SpotMarginClient { + return &SpotMarginClient{ + c: c, + isUta: isUta, + params: params, + } +} diff --git a/examples/spot_leverage/spot_leverage_token.go b/examples/spot_leverage/spot_leverage_token.go new file mode 100644 index 0000000..430af96 --- /dev/null +++ b/examples/spot_leverage/spot_leverage_token.go @@ -0,0 +1,22 @@ +package main + +import ( + "context" + "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" +) + +func main() { + PlaceTrade() +} + +func PlaceTrade() { + client := bybit.NewBybitHttpClient("8wYkmpLsMg10eNQyPm", "Ouxc34myDnXvei54XsBZgoQzfGxO4bkr2Zsj", bybit.WithBaseURL(bybit.TESTNET)) + params := map[string]interface{}{"ltCoin": "BTC3L"} + leverageTokenResult, err := client.NewSpotLeverageService(params).GetLeverageTokenInfo(context.Background()) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(bybit.PrettyPrint(leverageTokenResult)) +} diff --git a/examples/spot_margin/uta_margin_data.go b/examples/spot_margin/uta_margin_data.go new file mode 100644 index 0000000..0a63a14 --- /dev/null +++ b/examples/spot_margin/uta_margin_data.go @@ -0,0 +1,22 @@ +package main + +import ( + "context" + "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" +) + +func main() { + SpotMarginData() +} + +func SpotMarginData() { + client := bybit.NewBybitHttpClient("8wYkmpLsMg10eNQyPm", "Ouxc34myDnXvei54XsBZgoQzfGxO4bkr2Zsj", bybit.WithBaseURL(bybit.TESTNET)) + params := map[string]interface{}{"ltCoin": "BTC3L"} + leverageTokenResult, err := client.NewSpotMarginDataService(params, true).GetSpotMarginData(context.Background()) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(bybit.PrettyPrint(leverageTokenResult)) +} diff --git a/models/spotLeverageResponse.go b/models/spotLeverageResponse.go new file mode 100644 index 0000000..0466a39 --- /dev/null +++ b/models/spotLeverageResponse.go @@ -0,0 +1,78 @@ +package models + +import "time" + +// SpotLeverageTokenInfo holds the information for leveraged tokens. +type SpotLeverageTokenInfo struct { + List []struct { + LtCoin string `json:"ltCoin"` + LtName string `json:"ltName"` + MaxPurchase string `json:"maxPurchase"` + MinPurchase string `json:"minPurchase"` + MaxPurchaseDaily string `json:"maxPurchaseDaily"` + MaxRedeem string `json:"maxRedeem"` + MinRedeem string `json:"minRedeem"` + MaxRedeemDaily string `json:"maxRedeemDaily"` + PurchaseFeeRate string `json:"purchaseFeeRate"` + RedeemFeeRate string `json:"redeemFeeRate"` + LtStatus string `json:"ltStatus"` + FundFee string `json:"fundFee"` + FundFeeTime string `json:"fundFeeTime"` // Should parse to time.Time if needed + ManageFeeRate string `json:"manageFeeRate"` + ManageFeeTime string `json:"manageFeeTime"` // Should parse to time.Time if needed + Value string `json:"value"` + NetValue string `json:"netValue"` + Total string `json:"total"` + } +} + +// SpotLeverageTokenMarket holds the market information for a leveraged token. +type SpotLeverageTokenMarket struct { + LtCoin string `json:"ltCoin"` + Nav string `json:"nav"` + NavTime time.Time `json:"navTime"` // Assuming time is in milliseconds, need to convert + Circulation string `json:"circulation"` + Basket string `json:"basket"` + Leverage string `json:"leverage"` +} + +// SpotLeverageOrdersRecords holds the orders records for leveraged tokens. +type SpotLeverageOrdersRecords struct { + List []struct { + LtCoin string `json:"ltCoin"` + OrderId string `json:"orderId"` + LtOrderType int `json:"ltOrderType"` // Use int for integer type + OrderTime int64 `json:"orderTime"` // Assuming this is a timestamp, int64 is used + UpdateTime int64 `json:"updateTime"` // Assuming this is a timestamp, int64 is used + LtOrderStatus string `json:"ltOrderStatus"` + Fee string `json:"fee"` + Amount string `json:"amount"` + Value string `json:"value"` + ValueCoin string `json:"valueCoin"` + SerialNo string `json:"serialNo"` + } +} + +// LeverageTokenPurchaseResult represents the result of a leverage token purchase. +type LeverageTokenPurchaseResult struct { + LtCoin string `json:"ltCoin"` + LtOrderStatus string `json:"ltOrderStatus"` + ExecQty string `json:"execQty"` + ExecAmt string `json:"execAmt"` + Amount string `json:"amount"` + PurchaseId string `json:"purchaseId"` + SerialNo string `json:"serialNo"` + ValueCoin string `json:"valueCoin"` +} + +// LeverageTokenRedeem represents the redeem information for a leveraged token. +type LeverageTokenRedeem struct { + LtCoin string `json:"ltCoin"` + LtOrderStatus string `json:"ltOrderStatus"` + Quantity string `json:"quantity"` + ExecQty string `json:"execQty"` + ExecAmt string `json:"execAmt"` + RedeemId string `json:"redeemId"` + SerialNo string `json:"serialNo"` + ValueCoin string `json:"valueCoin"` +} diff --git a/models/spotMarginResponse.go b/models/spotMarginResponse.go new file mode 100644 index 0000000..fef246a --- /dev/null +++ b/models/spotMarginResponse.go @@ -0,0 +1,61 @@ +package models + +// SpotMarginDataResult holds data for spot margin including VIP level specific coin list. +type SpotMarginDataResult struct { + VipCoinList []struct { + List []struct { + Borrowable bool `json:"borrowable"` + CollateralRatio string `json:"collateralRatio"` + Currency string `json:"currency"` + HourlyBorrowRate string `json:"hourlyBorrowRate"` + LiquidationOrder string `json:"liquidationOrder"` + MarginCollateral bool `json:"marginCollateral"` + MaxBorrowingAmount string `json:"maxBorrowingAmount"` + } `json:"list"` + VipLevel string `json:"vipLevel"` + } `json:"vipCoinList"` +} + +// ClassicalSpotMarginCoinResult holds information about coins in classical spot margin. +type ClassicalSpotMarginCoinResult struct { + List []struct { + Coin string `json:"coin"` + ConversionRate string `json:"conversionRate"` + LiquidationOrder int `json:"liquidationOrder"` + } `json:"list"` +} + +// ClassicalSpotMarginBorrowCoinResult holds the borrowing precision information for coins in classical spot margin. +type ClassicalSpotMarginBorrowCoinResult struct { + List []struct { + Coin string `json:"coin"` + BorrowingPrecision int `json:"borrowingPrecision"` + RepaymentPrecision int `json:"repaymentPrecision"` + } `json:"list"` +} + +// ClassicalSpotMarginInterestResult contains information about the interest rate on spot margin. +type ClassicalSpotMarginInterestResult struct { + Coin string `json:"coin"` + InterestRate string `json:"interestRate"` + LoanAbleAmount string `json:"loanAbleAmount"` + MaxLoanAmount string `json:"maxLoanAmount"` +} + +// ClassicalSpotMarginLoanResult holds the loan account information in the classical spot margin. +type ClassicalSpotMarginLoanResult struct { + AcctBalanceSum string `json:"acctBalanceSum"` + DebtBalanceSum string `json:"debtBalanceSum"` + LoanAccountList []struct { + Free string `json:"free"` + Interest string `json:"interest"` + Loan string `json:"loan"` + RemainAmount string `json:"remainAmount"` + Locked string `json:"locked"` + TokenId string `json:"tokenId"` + Total string `json:"total"` + } `json:"loanAccountList"` + RiskRate string `json:"riskRate"` + Status int `json:"status"` // Use int for integer type + SwitchStatus int `json:"switchStatus"` // Use int for integer type +} diff --git a/spot_leverage.go b/spot_leverage.go new file mode 100644 index 0000000..c82d969 --- /dev/null +++ b/spot_leverage.go @@ -0,0 +1,123 @@ +package bybit_connector + +import ( + "context" + "encoding/json" + "github.com/wuhewuhe/bybit.go.api/handlers" + "net/http" +) + +type SpotLeverageClient struct { + c *Client + params map[string]interface{} +} + +func (s *SpotLeverageClient) GetLeverageTokenInfo(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/spot-lever-token/info", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotLeverageClient) GetLeverageTokenOrders(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/spot-lever-token/order-record", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotLeverageClient) GetLeverageTokenMarket(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/spot-lever-token/reference", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotLeverageClient) PurchaseLeverageToken(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodPost, + endpoint: "/v5/spot-lever-token/purchase", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotLeverageClient) RedeemLeverageToken(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodPost, + endpoint: "/v5/spot-lever-token/redeem", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/spot_margin.go b/spot_margin.go new file mode 100644 index 0000000..17a6ff1 --- /dev/null +++ b/spot_margin.go @@ -0,0 +1,143 @@ +package bybit_connector + +import ( + "context" + "encoding/json" + "errors" + "github.com/wuhewuhe/bybit.go.api/handlers" + "net/http" +) + +type SpotMarginClient struct { + c *Client + isUta bool + params map[string]interface{} +} + +func (s *SpotMarginClient) GetSpotMarginData(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + var endpoint string + if !s.isUta { + endpoint = "/v5/spot-margin-trade/data" + } else { + endpoint = "/v5/spot-cross-margin-trade/data" + } + r := &request{ + method: http.MethodGet, + endpoint: endpoint, + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotMarginClient) GetSpotMarginCoin(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if s.isUta { + return nil, errors.New("this function only works for classical accounts") + } + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/spot-cross-margin-trade/pledge-token", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotMarginClient) GetSpotMarginBorrowCoin(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if s.isUta { + return nil, errors.New("this function only works for classical accounts") + } + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/spot-cross-margin-trade/borrow-token", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotMarginClient) GetSpotMarginInterests(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if s.isUta { + return nil, errors.New("this function only works for classical accounts") + } + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/spot-cross-margin-trade/loan-info", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *SpotMarginClient) GetSpotMarginLoanAccountInfo(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if s.isUta { + return nil, errors.New("this function only works for classical accounts") + } + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/spot-cross-margin-trade/account", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +}