mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
binance: add get deposit address request
This commit is contained in:
parent
8cf9218dce
commit
c3c1666154
232
pkg/exchange/binance/binanceapi/client.go
Normal file
232
pkg/exchange/binance/binanceapi/client.go
Normal file
|
@ -0,0 +1,232 @@
|
|||
package binanceapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/requestgen"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
const defaultHTTPTimeout = time.Second * 15
|
||||
const RestBaseURL = "https://api.binance.com"
|
||||
const SandboxRestBaseURL = "https://testnet.binance.vision"
|
||||
|
||||
type RestClient struct {
|
||||
BaseURL *url.URL
|
||||
|
||||
client *http.Client
|
||||
|
||||
Key, Secret, Passphrase string
|
||||
KeyVersion string
|
||||
|
||||
recvWindow int
|
||||
timeOffset int64
|
||||
}
|
||||
|
||||
func NewClient() *RestClient {
|
||||
u, err := url.Parse(RestBaseURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
client := &RestClient{
|
||||
BaseURL: u,
|
||||
KeyVersion: "2",
|
||||
client: &http.Client{
|
||||
Timeout: defaultHTTPTimeout,
|
||||
},
|
||||
}
|
||||
|
||||
// client.AccountService = &AccountService{client: client}
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *RestClient) Auth(key, secret string) {
|
||||
c.Key = key
|
||||
c.Secret = secret
|
||||
}
|
||||
|
||||
// NewRequest create new API request. Relative url can be provided in refURL.
|
||||
func (c *RestClient) NewRequest(ctx context.Context, method, refURL string, params url.Values, payload interface{}) (*http.Request, error) {
|
||||
rel, err := url.Parse(refURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params != nil {
|
||||
rel.RawQuery = params.Encode()
|
||||
}
|
||||
|
||||
body, err := castPayload(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pathURL := c.BaseURL.ResolveReference(rel)
|
||||
return http.NewRequestWithContext(ctx, method, pathURL.String(), bytes.NewReader(body))
|
||||
}
|
||||
|
||||
// sendRequest sends the request to the API server and handle the response
|
||||
func (c *RestClient) SendRequest(req *http.Request) (*requestgen.Response, error) {
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// newResponse reads the response body and return a new Response object
|
||||
response, err := requestgen.NewResponse(resp)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
// Check error, if there is an error, return the ErrorResponse struct type
|
||||
if response.IsError() {
|
||||
return response, errors.New(string(response.Body))
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *RestClient) SetTimeOffsetFromServer(ctx context.Context) error {
|
||||
req, err := c.NewRequest(ctx, "GET", "/api/v3/time", nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.SendRequest(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var a struct {
|
||||
ServerTime types.MillisecondTimestamp `json:"serverTime"`
|
||||
}
|
||||
|
||||
err = resp.DecodeJSON(&a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.timeOffset = currentTimestamp() - a.ServerTime.Time().UnixMilli()
|
||||
return nil
|
||||
}
|
||||
|
||||
// newAuthenticatedRequest creates new http request for authenticated routes.
|
||||
func (c *RestClient) NewAuthenticatedRequest(ctx context.Context, method, refURL string, params url.Values, payload interface{}) (*http.Request, error) {
|
||||
if len(c.Key) == 0 {
|
||||
return nil, errors.New("empty api key")
|
||||
}
|
||||
|
||||
if len(c.Secret) == 0 {
|
||||
return nil, errors.New("empty api secret")
|
||||
}
|
||||
|
||||
rel, err := url.Parse(refURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params == nil {
|
||||
params = url.Values{}
|
||||
}
|
||||
|
||||
if c.recvWindow > 0 {
|
||||
params.Set("recvWindow", strconv.Itoa(c.recvWindow))
|
||||
}
|
||||
|
||||
params.Set("timestamp", strconv.FormatInt(currentTimestamp()-c.timeOffset, 10))
|
||||
rawQuery := params.Encode()
|
||||
|
||||
pathURL := c.BaseURL.ResolveReference(rel)
|
||||
body, err := castPayload(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toSign := rawQuery + string(body)
|
||||
signature := sign(c.Secret, toSign)
|
||||
|
||||
// sv is the extra url parameters that we need to attach to the request
|
||||
sv := url.Values{}
|
||||
sv.Set("signature", signature)
|
||||
if rawQuery == "" {
|
||||
rawQuery = sv.Encode()
|
||||
} else {
|
||||
rawQuery = rawQuery + "&" + sv.Encode()
|
||||
}
|
||||
|
||||
if rawQuery != "" {
|
||||
pathURL.RawQuery = rawQuery
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, pathURL.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if our payload body is not an empty string
|
||||
if len(body) > 0 {
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
// Build authentication headers
|
||||
req.Header.Add("X-MBX-APIKEY", c.Key)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// sign uses sha256 to sign the payload with the given secret
|
||||
func sign(secret, payload string) string {
|
||||
var sig = hmac.New(sha256.New, []byte(secret))
|
||||
_, err := sig.Write([]byte(payload))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", sig.Sum(nil))
|
||||
}
|
||||
|
||||
func currentTimestamp() int64 {
|
||||
return FormatTimestamp(time.Now())
|
||||
}
|
||||
|
||||
// FormatTimestamp formats a time into Unix timestamp in milliseconds, as requested by Binance.
|
||||
func FormatTimestamp(t time.Time) int64 {
|
||||
return t.UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
func castPayload(payload interface{}) ([]byte, error) {
|
||||
if payload != nil {
|
||||
switch v := payload.(type) {
|
||||
case string:
|
||||
return []byte(v), nil
|
||||
|
||||
case []byte:
|
||||
return v, nil
|
||||
|
||||
default:
|
||||
body, err := json.Marshal(v)
|
||||
return body, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type APIResponse struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"msg"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
91
pkg/exchange/binance/binanceapi/client_test.go
Normal file
91
pkg/exchange/binance/binanceapi/client_test.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package binanceapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func maskSecret(s string) string {
|
||||
re := regexp.MustCompile(`\b(\w{4})\w+\b`)
|
||||
s = re.ReplaceAllString(s, "$1******")
|
||||
return s
|
||||
}
|
||||
|
||||
func integrationTestConfigured(t *testing.T, prefix string) (key, secret string, ok bool) {
|
||||
var hasKey, hasSecret bool
|
||||
key, hasKey = os.LookupEnv(prefix + "_API_KEY")
|
||||
secret, hasSecret = os.LookupEnv(prefix + "_API_SECRET")
|
||||
ok = hasKey && hasSecret && os.Getenv("TEST_"+prefix) == "1"
|
||||
if ok {
|
||||
t.Logf(prefix+" api integration test enabled, key = %s, secret = %s", maskSecret(key), maskSecret(secret))
|
||||
}
|
||||
return key, secret, ok
|
||||
}
|
||||
|
||||
func TestClient_GetTradeFeeRequest(t *testing.T) {
|
||||
key, secret, ok := integrationTestConfigured(t, "BINANCE")
|
||||
if !ok {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
client := NewClient()
|
||||
client.Auth(key, secret)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err := client.SetTimeOffsetFromServer(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := client.NewGetTradeFeeRequest()
|
||||
tradeFees, err := req.Do(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, tradeFees)
|
||||
t.Logf("tradeFees: %+v", tradeFees)
|
||||
}
|
||||
|
||||
func TestClient_privateCall(t *testing.T) {
|
||||
key, secret, ok := integrationTestConfigured(t, "BINANCE")
|
||||
if !ok {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
client := NewClient()
|
||||
client.Auth(key, secret)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err := client.SetTimeOffsetFromServer(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req, err := client.NewAuthenticatedRequest(ctx, "GET", "/sapi/v1/asset/tradeFee", nil, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, req)
|
||||
|
||||
resp, err := client.SendRequest(req)
|
||||
if assert.NoError(t, err) {
|
||||
var feeStructs []struct{
|
||||
Symbol string `json:"symbol"`
|
||||
MakerCommission string `json:"makerCommission"`
|
||||
TakerCommission string `json:"takerCommission"`
|
||||
}
|
||||
err = resp.DecodeJSON(&feeStructs)
|
||||
if assert.NoError(t, err) {
|
||||
assert.NotEmpty(t, feeStructs)
|
||||
}
|
||||
} else {
|
||||
dump, _ := httputil.DumpRequest(req, true);
|
||||
log.Printf("request: %s", dump)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_setTimeOffsetFromServer(t *testing.T) {
|
||||
client := NewClient()
|
||||
err := client.SetTimeOffsetFromServer(context.Background())
|
||||
assert.NoError(t, err)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package binanceapi
|
||||
|
||||
import (
|
||||
"github.com/c9s/requestgen"
|
||||
)
|
||||
|
||||
//go:generate -command GetRequest requestgen -method GET
|
||||
//go:generate -command PostRequest requestgen -method POST
|
||||
//go:generate -command DeleteRequest requestgen -method DELETE
|
||||
|
||||
type DepositAddress struct {
|
||||
Address string `json:"address"`
|
||||
Coin string `json:"coin"`
|
||||
Tag string `json:"tag"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
|
||||
//go:generate GetRequest -url "/sapi/v1/capital/deposit/address" -type GetDepositAddressRequest -responseType .DepositAddress
|
||||
type GetDepositAddressRequest struct {
|
||||
client requestgen.AuthenticatedAPIClient
|
||||
|
||||
coin string `param:"coin"`
|
||||
|
||||
network *string `param:"network"`
|
||||
}
|
||||
|
||||
func (c *RestClient) NewGetDepositAddressRequest() *GetDepositAddressRequest {
|
||||
return &GetDepositAddressRequest{client: c}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// Code generated by "requestgen -method GET -url /sapi/v1/capital/deposit/address -type GetDepositAddressRequest -responseType .DepositAddress"; DO NOT EDIT.
|
||||
|
||||
package binanceapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// GetQueryParameters builds and checks the query parameters and returns url.Values
|
||||
func (g *GetDepositAddressRequest) GetQueryParameters() (url.Values, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
query := url.Values{}
|
||||
for k, v := range params {
|
||||
query.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParameters builds and checks the parameters and return the result in a map object
|
||||
func (g *GetDepositAddressRequest) GetParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
|
||||
func (g *GetDepositAddressRequest) GetParametersQuery() (url.Values, error) {
|
||||
query := url.Values{}
|
||||
|
||||
params, err := g.GetParameters()
|
||||
if err != nil {
|
||||
return query, err
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
if g.isVarSlice(v) {
|
||||
g.iterateSlice(v, func(it interface{}) {
|
||||
query.Add(k+"[]", fmt.Sprintf("%v", it))
|
||||
})
|
||||
} else {
|
||||
query.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParametersJSON converts the parameters from GetParameters into the JSON format
|
||||
func (g *GetDepositAddressRequest) GetParametersJSON() ([]byte, error) {
|
||||
params, err := g.GetParameters()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(params)
|
||||
}
|
||||
|
||||
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
|
||||
func (g *GetDepositAddressRequest) GetSlugParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (g *GetDepositAddressRequest) applySlugsToUrl(url string, slugs map[string]string) string {
|
||||
for k, v := range slugs {
|
||||
needleRE := regexp.MustCompile(":" + k + "\\b")
|
||||
url = needleRE.ReplaceAllString(url, v)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func (g *GetDepositAddressRequest) iterateSlice(slice interface{}, f func(it interface{})) {
|
||||
sliceValue := reflect.ValueOf(slice)
|
||||
for i := 0; i < sliceValue.Len(); i++ {
|
||||
it := sliceValue.Index(i).Interface()
|
||||
f(it)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GetDepositAddressRequest) isVarSlice(v interface{}) bool {
|
||||
rt := reflect.TypeOf(v)
|
||||
switch rt.Kind() {
|
||||
case reflect.Slice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GetDepositAddressRequest) GetSlugsMap() (map[string]string, error) {
|
||||
slugs := map[string]string{}
|
||||
params, err := g.GetSlugParameters()
|
||||
if err != nil {
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
slugs[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
func (g *GetDepositAddressRequest) Do(ctx context.Context) (*DepositAddress, error) {
|
||||
|
||||
// no body params
|
||||
var params interface{}
|
||||
query := url.Values{}
|
||||
|
||||
apiURL := "/sapi/v1/capital/deposit/address"
|
||||
|
||||
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := g.client.SendRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var apiResponse DepositAddress
|
||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apiResponse, nil
|
||||
}
|
26
pkg/exchange/binance/binanceapi/get_trade_fee_request.go
Normal file
26
pkg/exchange/binance/binanceapi/get_trade_fee_request.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package binanceapi
|
||||
|
||||
import (
|
||||
"github.com/c9s/requestgen"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
//go:generate -command GetRequest requestgen -method GET
|
||||
//go:generate -command PostRequest requestgen -method POST
|
||||
//go:generate -command DeleteRequest requestgen -method DELETE
|
||||
|
||||
type TradeFee struct {
|
||||
Symbol string `json:"symbol"`
|
||||
MakerCommission fixedpoint.Value `json:"makerCommission"`
|
||||
TakerCommission fixedpoint.Value `json:"takerCommission"`
|
||||
}
|
||||
|
||||
//go:generate GetRequest -url "/sapi/v1/asset/tradeFee" -type GetTradeFeeRequest -responseType []TradeFee
|
||||
type GetTradeFeeRequest struct {
|
||||
client requestgen.AuthenticatedAPIClient
|
||||
}
|
||||
|
||||
func (c *RestClient) NewGetTradeFeeRequest() *GetTradeFeeRequest {
|
||||
return &GetTradeFeeRequest{client: c}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// Code generated by "requestgen -method GET -url /sapi/v1/asset/tradeFee -type GetTradeFeeRequest -responseType []TradeFee"; DO NOT EDIT.
|
||||
|
||||
package binanceapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// GetQueryParameters builds and checks the query parameters and returns url.Values
|
||||
func (g *GetTradeFeeRequest) GetQueryParameters() (url.Values, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
query := url.Values{}
|
||||
for k, v := range params {
|
||||
query.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParameters builds and checks the parameters and return the result in a map object
|
||||
func (g *GetTradeFeeRequest) GetParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
|
||||
func (g *GetTradeFeeRequest) GetParametersQuery() (url.Values, error) {
|
||||
query := url.Values{}
|
||||
|
||||
params, err := g.GetParameters()
|
||||
if err != nil {
|
||||
return query, err
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
if g.isVarSlice(v) {
|
||||
g.iterateSlice(v, func(it interface{}) {
|
||||
query.Add(k+"[]", fmt.Sprintf("%v", it))
|
||||
})
|
||||
} else {
|
||||
query.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// GetParametersJSON converts the parameters from GetParameters into the JSON format
|
||||
func (g *GetTradeFeeRequest) GetParametersJSON() ([]byte, error) {
|
||||
params, err := g.GetParameters()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(params)
|
||||
}
|
||||
|
||||
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
|
||||
func (g *GetTradeFeeRequest) GetSlugParameters() (map[string]interface{}, error) {
|
||||
var params = map[string]interface{}{}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (g *GetTradeFeeRequest) applySlugsToUrl(url string, slugs map[string]string) string {
|
||||
for k, v := range slugs {
|
||||
needleRE := regexp.MustCompile(":" + k + "\\b")
|
||||
url = needleRE.ReplaceAllString(url, v)
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func (g *GetTradeFeeRequest) iterateSlice(slice interface{}, f func(it interface{})) {
|
||||
sliceValue := reflect.ValueOf(slice)
|
||||
for i := 0; i < sliceValue.Len(); i++ {
|
||||
it := sliceValue.Index(i).Interface()
|
||||
f(it)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GetTradeFeeRequest) isVarSlice(v interface{}) bool {
|
||||
rt := reflect.TypeOf(v)
|
||||
switch rt.Kind() {
|
||||
case reflect.Slice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GetTradeFeeRequest) GetSlugsMap() (map[string]string, error) {
|
||||
slugs := map[string]string{}
|
||||
params, err := g.GetSlugParameters()
|
||||
if err != nil {
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
slugs[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
return slugs, nil
|
||||
}
|
||||
|
||||
func (g *GetTradeFeeRequest) Do(ctx context.Context) ([]TradeFee, error) {
|
||||
|
||||
// no body params
|
||||
var params interface{}
|
||||
query := url.Values{}
|
||||
|
||||
apiURL := "/sapi/v1/asset/tradeFee"
|
||||
|
||||
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := g.client.SendRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var apiResponse []TradeFee
|
||||
if err := response.DecodeJSON(&apiResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiResponse, nil
|
||||
}
|
|
@ -279,6 +279,17 @@ func (e *Exchange) BorrowMarginAsset(ctx context.Context, asset string, amount f
|
|||
return err
|
||||
}
|
||||
|
||||
func (e *Exchange) QueryMarginBorrowHistory(ctx context.Context, asset string) error {
|
||||
req := e.client.NewListMarginLoansService()
|
||||
req.Asset(asset)
|
||||
history, err := req.Do(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = history
|
||||
return nil
|
||||
}
|
||||
|
||||
// transferCrossMarginAccountAsset transfer asset to the cross margin account or to the main account
|
||||
func (e *Exchange) transferCrossMarginAccountAsset(ctx context.Context, asset string, amount fixedpoint.Value, io int) error {
|
||||
req := e.client.NewMarginTransferService()
|
||||
|
|
Loading…
Reference in New Issue
Block a user