1
This commit is contained in:
282
common/helper/binancehttphelper.go
Normal file
282
common/helper/binancehttphelper.go
Normal file
@ -0,0 +1,282 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
type BinanceClient struct {
|
||||
APIKey string
|
||||
APISecret string
|
||||
HTTPClient *http.Client
|
||||
SpotBaseURL string //现货api地址
|
||||
FutBaseURL string //合约api地址
|
||||
}
|
||||
|
||||
// NewBinanceClient creates a new Binance client
|
||||
func NewBinanceClient(apiKey, apiSecret string, proxyType, proxyAddr string) (*BinanceClient, error) {
|
||||
// Create HTTP client with transport settings
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 1000,
|
||||
IdleConnTimeout: 10 * time.Second, // 设置超时 10 秒
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
err := CreateHtppProxy(proxyType, proxyAddr, client)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BinanceClient{
|
||||
APIKey: apiKey,
|
||||
APISecret: apiSecret,
|
||||
HTTPClient: client,
|
||||
SpotBaseURL: "https://api.binance.com",
|
||||
FutBaseURL: "https://fapi.binance.com",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Helper to get proxy password from URL
|
||||
func getProxyPassword(proxyURL *url.URL) string {
|
||||
if password, ok := proxyURL.User.Password(); ok {
|
||||
return password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// signRequest generates HMAC SHA256 signature
|
||||
func (bc *BinanceClient) signRequest(query string) string {
|
||||
mac := hmac.New(sha256.New, []byte(bc.APISecret))
|
||||
mac.Write([]byte(query))
|
||||
return hex.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotRequest(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotRequestAuth(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotAuth(endpoint string, method string, params interface{}) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 现货请求 只需要api key
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendSpotRequestByKey(endpoint string, method string, params interface{}) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.SpotBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 1)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesRequest(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesRequestAuth(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesRequestByKey(endpoint string, method string, params map[string]string) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 1)
|
||||
}
|
||||
|
||||
/*
|
||||
binance 合约请求
|
||||
|
||||
- @endpoint 路由
|
||||
- @method get、post
|
||||
- @params 参数map
|
||||
*/
|
||||
func (bc *BinanceClient) SendFuturesAuth(endpoint string, method string, params interface{}) ([]byte, int, error) {
|
||||
// Prepare URL
|
||||
reqURL := bc.FutBaseURL + endpoint
|
||||
|
||||
return bc.SendRequest(reqURL, method, params, 2)
|
||||
}
|
||||
|
||||
// SendRequest sends a request to Binance API
|
||||
/*
|
||||
发送请求
|
||||
- auth 0-不需要 1-只需要key 2-需要key和签名
|
||||
*/
|
||||
func (bc *BinanceClient) SendRequest(reqURL string, method string, params interface{}, auth int) ([]byte, int, error) {
|
||||
method = strings.ToUpper(method)
|
||||
reqParams := url.Values{}
|
||||
|
||||
// 处理 `params`,如果是 map[string]string 则添加为 URL 参数
|
||||
if paramMap, ok := params.(map[string]string); ok {
|
||||
for k, v := range paramMap {
|
||||
reqParams.Add(k, v)
|
||||
}
|
||||
} else if v := reflect.ValueOf(params); v.Kind() == reflect.Struct {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Type().Field(i)
|
||||
value := v.Field(i)
|
||||
|
||||
// 获取字段名或 JSON 标签名
|
||||
key := field.Tag.Get("json")
|
||||
if key == "" {
|
||||
key = field.Name // 如果没有 json 标签,使用字段名
|
||||
}
|
||||
|
||||
// 检查字段类型并转换为对应的字符串
|
||||
var strValue string
|
||||
switch value.Kind() {
|
||||
case reflect.String:
|
||||
strValue = value.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
strValue = strconv.FormatInt(value.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
strValue = strconv.FormatUint(value.Uint(), 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
strValue = strconv.FormatFloat(value.Float(), 'f', -1, 64)
|
||||
case reflect.Bool:
|
||||
strValue = strconv.FormatBool(value.Bool())
|
||||
case reflect.Struct:
|
||||
// 处理 decimal.Decimal 类型
|
||||
if value.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||
strValue = value.Interface().(decimal.Decimal).String()
|
||||
} else {
|
||||
continue // 跳过其他 struct 类型
|
||||
}
|
||||
default:
|
||||
continue // 跳过不支持的类型
|
||||
}
|
||||
// 添加到 reqParams
|
||||
reqParams.Add(key, strValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Add timestamp if signature is needed
|
||||
if auth == 2 && bc.APIKey != "" && bc.APISecret != "" {
|
||||
reqParams.Add("timestamp", fmt.Sprintf("%d", time.Now().UnixMilli()))
|
||||
signature := bc.signRequest(reqParams.Encode())
|
||||
reqParams.Add("signature", signature)
|
||||
}
|
||||
// Create HTTP request
|
||||
var req *http.Request
|
||||
var err error
|
||||
if method == http.MethodGet || method == http.MethodDelete {
|
||||
if len(reqParams) > 0 {
|
||||
reqURL = fmt.Sprintf("%s?%s", reqURL, reqParams.Encode())
|
||||
}
|
||||
req, err = http.NewRequest(method, reqURL, nil)
|
||||
} else {
|
||||
// req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
if len(reqParams) > 0 {
|
||||
reqURL = fmt.Sprintf("%s?%s", reqURL, reqParams.Encode())
|
||||
}
|
||||
req, err = http.NewRequest(method, reqURL, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
// Set headers
|
||||
if auth > 0 && bc.APIKey != "" {
|
||||
req.Header.Set("X-MBX-APIKEY", bc.APIKey)
|
||||
}
|
||||
// req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
// Send request
|
||||
resp, err := bc.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("failed to send request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read response
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, -1, fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, resp.StatusCode, fmt.Errorf("%s", body)
|
||||
}
|
||||
|
||||
return body, http.StatusOK, nil
|
||||
}
|
||||
Reference in New Issue
Block a user