1
This commit is contained in:
299
services/excservice/biance.go
Normal file
299
services/excservice/biance.go
Normal file
@ -0,0 +1,299 @@
|
||||
package excservice
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
"go-admin/models"
|
||||
"go-admin/pkg/httputils"
|
||||
"go-admin/pkg/utility"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
TICKER_URI = "/api/v3/ticker/24hr?symbol=%s"
|
||||
TICKERS_URI = "ticker/allBookTickers"
|
||||
DEPTH_URI = "/api/v3/depth?symbol=%s&limit=%d"
|
||||
ACCOUNT_URI = "/api/v3/account?"
|
||||
ORDER_URI = "/api/v3/order"
|
||||
UNFINISHED_ORDERS_INFO = "openOrders?"
|
||||
KLINE_URI = "klines"
|
||||
SERVER_TIME_URL = "/api/v3/time"
|
||||
)
|
||||
|
||||
var (
|
||||
apiUrl = "https://api.binance.com"
|
||||
//如果上面的baseURL访问有性能问题,请访问下面的API集群:
|
||||
//https://api1.binance.com
|
||||
//https://api2.binance.com
|
||||
//https://api3.binance.com
|
||||
)
|
||||
|
||||
func init() {
|
||||
//err := setTimeOffsetPro()
|
||||
//if err != nil {
|
||||
// fmt.Println("setTimeOffsetPro,err:", err)
|
||||
//}
|
||||
}
|
||||
|
||||
func buildParamsSigned(postForm *url.Values, secretKey string) {
|
||||
postForm.Set("recvWindow", "60000")
|
||||
tonce := strconv.FormatInt(time.Now().UnixNano()+timeOffset, 10)[0:13]
|
||||
postForm.Set("timestamp", tonce)
|
||||
payload := postForm.Encode()
|
||||
sign, _ := GetHmacSHA256Sign(secretKey, payload)
|
||||
postForm.Set("signature", sign)
|
||||
}
|
||||
func GetHmacSHA256Sign(secret, params string) (string, error) {
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
_, err := mac.Write([]byte(params))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(mac.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// 获取服务器时间
|
||||
func setTimeOffsetPro() error {
|
||||
respData, err := httputils.NewHttpRequestWithFasthttp("GET", apiUrl+SERVER_TIME_URL, "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var bodyDataMap map[string]interface{}
|
||||
err = json.Unmarshal(respData, &bodyDataMap)
|
||||
if err != nil {
|
||||
log.Error(string(respData))
|
||||
return err
|
||||
}
|
||||
stime := int64(utility.ToInt(bodyDataMap["serverTime"]))
|
||||
st := time.Unix(stime/1000, 1000000*(stime%1000))
|
||||
lt := time.Now()
|
||||
offset := st.Sub(lt).Nanoseconds()
|
||||
timeOffset = offset
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTicker 获取24小时行情
|
||||
func GetTicker(coin, currency string) (models.Ticker24, error) {
|
||||
par := strings.ToUpper(coin + currency)
|
||||
tickerUri := apiUrl + fmt.Sprintf(TICKER_URI, par)
|
||||
var ticker models.Ticker24
|
||||
respData, err := httputils.NewHttpRequestWithFasthttp("GET", tickerUri, "", nil)
|
||||
if err != nil {
|
||||
log.Error("GetTicker", zap.Error(err))
|
||||
return ticker, err
|
||||
}
|
||||
var tickerMap map[string]interface{}
|
||||
err = json.Unmarshal(respData, &tickerMap)
|
||||
if err != nil {
|
||||
log.Error("GetTicker", zap.ByteString("respData", respData), zap.Error(err))
|
||||
return ticker, err
|
||||
}
|
||||
|
||||
ticker.LastPrice = tickerMap["lastPrice"].(string)
|
||||
ticker.LowPrice = tickerMap["lowPrice"].(string)
|
||||
ticker.HighPrice = tickerMap["highPrice"].(string)
|
||||
ticker.Volume = tickerMap["volume"].(string)
|
||||
ticker.QuoteVolume = tickerMap["quoteVolume"].(string)
|
||||
ticker.ChangePercent = tickerMap["priceChangePercent"].(string)
|
||||
ticker.OpenPrice = tickerMap["openPrice"].(string)
|
||||
return ticker, nil
|
||||
}
|
||||
|
||||
// GetTickerBySymbols 获取24小时行情 symbols symbols参数可接受的格式: ["BTCUSDT","BNBUSDT"]
|
||||
func GetTickerBySymbols(symbols string) ([]models.Ticker24, error) {
|
||||
tickerUri := apiUrl + "/api/v3/ticker/24hr"
|
||||
|
||||
respData, err := httputils.NewHttpRequestWithFasthttp("GET", tickerUri, "", nil)
|
||||
if err != nil {
|
||||
log.Error("GetTicker", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
var tickerList []interface{}
|
||||
err = json.Unmarshal(respData, &tickerList)
|
||||
if err != nil {
|
||||
log.Error("GetTickerBySymbols", zap.ByteString("respData", respData), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
list := make([]models.Ticker24, 0, len(tickerList))
|
||||
for _, t := range tickerList {
|
||||
tickerMap := t.(map[string]interface{})
|
||||
if tickerMap == nil {
|
||||
continue
|
||||
}
|
||||
var ticker models.Ticker24
|
||||
ticker.LastPrice = tickerMap["lastPrice"].(string)
|
||||
ticker.LowPrice = tickerMap["lowPrice"].(string)
|
||||
ticker.HighPrice = tickerMap["highPrice"].(string)
|
||||
ticker.Volume = tickerMap["volume"].(string)
|
||||
ticker.QuoteVolume = tickerMap["quoteVolume"].(string)
|
||||
ticker.ChangePercent = tickerMap["priceChangePercent"].(string)
|
||||
ticker.OpenPrice = tickerMap["openPrice"].(string)
|
||||
ticker.Symbol = tickerMap["symbol"].(string)
|
||||
list = append(list, ticker)
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// GetKlinePro 获取k线--现货行情接口
|
||||
func GetKlinePro(coin, currency string, period string, size int) ([]models.Kline, error) {
|
||||
par := strings.ToUpper(coin + currency)
|
||||
periodS := period //, isOk := INERNAL_KLINE_PERIOD_CONVERTER[period]
|
||||
//if isOk != true {
|
||||
// periodS = "M1"
|
||||
//}
|
||||
key := fmt.Sprintf("%s:%s:%s", global.K_SPOT, par, period)
|
||||
|
||||
//获取缓存
|
||||
klineStrs, err := helper.DefaultRedis.GetAllSortSet(key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(klineStrs) > 0 && len(klineStrs) >= 500 {
|
||||
klines := make([]models.Kline, 0)
|
||||
|
||||
for _, item := range klineStrs {
|
||||
var kline models.Kline
|
||||
err := sonic.Unmarshal([]byte(item), &kline)
|
||||
|
||||
if err == nil {
|
||||
klines = append(klines, kline)
|
||||
}
|
||||
}
|
||||
|
||||
return klines, nil
|
||||
}
|
||||
|
||||
//没有缓存 重新获取
|
||||
|
||||
urlKline := apiUrl + "/api/v3/klines?symbol=" + par + "&interval=" + periodS + "&limit=" + utility.IntTostring(size)
|
||||
respData, err := httputils.NewHttpRequestWithFasthttp("GET", urlKline, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var bodyDataMap []interface{}
|
||||
err = json.Unmarshal(respData, &bodyDataMap)
|
||||
if err != nil {
|
||||
log.Error("GetKlinePro", zap.ByteString("respData", respData), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
var klines []models.Kline
|
||||
for _, _record := range bodyDataMap {
|
||||
r := models.Kline{}
|
||||
record := _record.([]interface{})
|
||||
times := utility.ToFloat64(record[0]) //to unix timestramp
|
||||
|
||||
// 超出10位的 处理为
|
||||
if times > 9999999999 {
|
||||
r.Timestamp = int64(times) / 1000
|
||||
} else {
|
||||
r.Timestamp = int64(times)
|
||||
}
|
||||
|
||||
r.Open = record[1].(string)
|
||||
r.High = record[2].(string)
|
||||
r.Low = record[3].(string)
|
||||
r.Close = record[4].(string)
|
||||
r.Vol = record[5].(string)
|
||||
r.QuoteVolume = record[7].(string)
|
||||
|
||||
klines = append(klines, r)
|
||||
|
||||
member, err := sonic.Marshal(r)
|
||||
|
||||
if err == nil {
|
||||
err = helper.DefaultRedis.SignelAdd(key, float64(r.Timestamp), string(member))
|
||||
|
||||
if err != nil {
|
||||
log.Error("保存k线数据失败:", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return klines, nil
|
||||
}
|
||||
|
||||
// GetTrades 非个人,整个交易所的交易记录
|
||||
// 注意:since is fromId
|
||||
func GetTrades(coin, currency string) ([]models.NewDealPush, error) {
|
||||
param := url.Values{}
|
||||
param.Set("symbol", strings.ToUpper(coin+currency))
|
||||
param.Set("limit", "50")
|
||||
//if since > 0 {
|
||||
// param.Set("fromId", strconv.Itoa(int(since)))
|
||||
//}
|
||||
urlTrade := apiUrl + "/api/v3/trades?" + param.Encode()
|
||||
resp, err := httputils.NewHttpRequestWithFasthttp("GET", urlTrade, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var bodyDataMap []interface{}
|
||||
err = json.Unmarshal(resp, &bodyDataMap)
|
||||
if err != nil {
|
||||
log.Error("GetTrades", zap.ByteString("respData", resp), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
var trades []models.NewDealPush
|
||||
for _, v := range bodyDataMap {
|
||||
m := v.(map[string]interface{})
|
||||
ty := 2
|
||||
if m["isBuyerMaker"].(bool) {
|
||||
ty = 1
|
||||
}
|
||||
trades = append(trades, models.NewDealPush{
|
||||
DealId: utility.ToInt64(m["id"]),
|
||||
Type: ty,
|
||||
Num: utility.ToFloat64(m["qty"]),
|
||||
Price: utility.ToFloat64(m["price"]),
|
||||
CreateTime: utility.ToInt64(m["time"]),
|
||||
})
|
||||
}
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
// GetDepth 获取深度
|
||||
func GetDepth(size int, coin, currency string) (models.DepthBin, error) {
|
||||
if size <= 5 {
|
||||
size = 5
|
||||
} else if size <= 10 {
|
||||
size = 10
|
||||
} else if size <= 20 {
|
||||
size = 20
|
||||
} else if size <= 50 {
|
||||
size = 50
|
||||
} else if size <= 100 {
|
||||
size = 100
|
||||
} else if size <= 500 {
|
||||
size = 500
|
||||
} else {
|
||||
size = 1000
|
||||
}
|
||||
urlDep := fmt.Sprintf(apiUrl+DEPTH_URI, strings.ToUpper(coin+currency), size)
|
||||
respFive, err := httputils.NewHttpRequestWithFasthttp("GET", urlDep, "", nil)
|
||||
if err != nil {
|
||||
return models.DepthBin{}, err
|
||||
}
|
||||
d := models.DepthBin{}
|
||||
err = sonic.Unmarshal(respFive, &d)
|
||||
if err != nil {
|
||||
fmt.Println("GetDepth json unmarshal error for ", string(respFive), zap.Error(err))
|
||||
return models.DepthBin{}, err
|
||||
}
|
||||
return d, nil
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user