Files
2025-02-06 11:14:33 +08:00

300 lines
8.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}