From 7feb3fa1bcbcf4457c1f4b58410a633432eafa18 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Tue, 8 Dec 2020 18:12:20 +0800 Subject: [PATCH 01/34] [binance] adapt error type --- binance/Binance.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/binance/Binance.go b/binance/Binance.go index 9f7e1e71..4d14d185 100644 --- a/binance/Binance.go +++ b/binance/Binance.go @@ -428,7 +428,7 @@ func (bn *Binance) CancelOrder(orderId string, currencyPair CurrencyPair) (bool, resp, err := HttpDeleteForm(bn.httpClient, path, params, map[string]string{"X-MBX-APIKEY": bn.accessKey}) if err != nil { - return false, err + return false, bn.adaptError(err) } respmap := make(map[string]interface{}) @@ -706,3 +706,22 @@ func (bn *Binance) GetTradeSymbol(currencyPair CurrencyPair) (*TradeSymbol, erro } return nil, errors.New("symbol not found") } + +func (bn *Binance) adaptError(err error) error { + errStr := err.Error() + + if strings.Contains(errStr, "Order does not exist") || + strings.Contains(errStr, "Unknown order sent") { + return EX_ERR_NOT_FIND_ORDER.OriginErr(errStr) + } + + if strings.Contains(errStr, "Too much request") { + return EX_ERR_API_LIMIT.OriginErr(errStr) + } + + if strings.Contains(errStr, "insufficient") { + return EX_ERR_INSUFFICIENT_BALANCE.OriginErr(errStr) + } + + return err +} From c437a62f50feb4202de3a2bd5d53922f8c87ab0a Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Wed, 9 Dec 2020 15:27:48 +0800 Subject: [PATCH 02/34] [binance] spot ws --- binance/Adapter.go | 25 ++++++ binance/SpotWs.go | 180 +++++++++++++++++++++++++++++++++++++++++ binance/SpotWs_test.go | 32 ++++++++ builder/APIBuilder.go | 2 + websocket.go | 1 + 5 files changed, 240 insertions(+) create mode 100644 binance/Adapter.go create mode 100644 binance/SpotWs.go create mode 100644 binance/SpotWs_test.go diff --git a/binance/Adapter.go b/binance/Adapter.go new file mode 100644 index 00000000..c9e457d5 --- /dev/null +++ b/binance/Adapter.go @@ -0,0 +1,25 @@ +package binance + +import ( + "fmt" + "github.com/nntaoli-project/goex" + "strings" +) + +func adaptStreamToCurrencyPair(stream string) goex.CurrencyPair { + symbol := strings.Split(stream, "@")[0] + + if strings.HasSuffix(symbol, "usdt") { + return goex.NewCurrencyPair2(fmt.Sprintf("%s_usdt", strings.TrimSuffix(symbol, "usdt"))) + } + + if strings.HasSuffix(symbol, "usd") { + return goex.NewCurrencyPair2(fmt.Sprintf("%s_usd", strings.TrimSuffix(symbol, "usd"))) + } + + if strings.HasSuffix(symbol, "btc") { + return goex.NewCurrencyPair2(fmt.Sprintf("%s_btc", strings.TrimSuffix(symbol, "btc"))) + } + + return goex.UNKNOWN_PAIR +} diff --git a/binance/SpotWs.go b/binance/SpotWs.go new file mode 100644 index 00000000..47169cb2 --- /dev/null +++ b/binance/SpotWs.go @@ -0,0 +1,180 @@ +package binance + +import ( + json2 "encoding/json" + "fmt" + "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "os" + "sort" + "strings" + "time" +) + +type req struct { + Method string `json:"method"` + Params []string `json:"params"` + Id int `json:"id"` +} + +type resp struct { + Stream string `json:"stream"` + Data json2.RawMessage `json:"data"` +} + +type depthResp struct { + LastUpdateId int `json:"lastUpdateId"` + Bids [][]interface{} `json:"bids"` + Asks [][]interface{} `json:"asks"` +} + +type SpotWs struct { + c *goex.WsConn + + reqId int + + depthCallFn func(depth *goex.Depth) + tickerCallFn func(ticker *goex.Ticker) + tradeCallFn func(trade *goex.Trade) +} + +func NewSpotWs() *SpotWs { + spotWs := &SpotWs{} + logger.Debugf("proxy url: %s", os.Getenv("HTTPS_PROXY")) + + wsBuilder := goex.NewWsBuilder(). + WsUrl("wss://stream.binance.com:9443/stream?streams=depth/miniTicker/ticker/trade"). + ProxyUrl(os.Getenv("HTTPS_PROXY")). + ProtoHandleFunc(spotWs.handle).AutoReconnect() + + spotWs.c = wsBuilder.Build() + spotWs.reqId = 1 + + return spotWs +} + +func (s *SpotWs) DepthCallback(f func(depth *goex.Depth)) { + s.depthCallFn = f +} + +func (s *SpotWs) TickerCallback(f func(ticker *goex.Ticker)) { + s.tickerCallFn = f +} + +func (s *SpotWs) TradeCallback(f func(trade *goex.Trade)) { + s.tradeCallFn = f +} + +func (s *SpotWs) SubscribeDepth(pair goex.CurrencyPair) error { + defer func() { + s.reqId++ + }() + + return s.c.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{ + fmt.Sprintf("%s@depth10@100ms", pair.ToLower().ToSymbol("")), + }, + Id: s.reqId, + }) +} + +func (s *SpotWs) SubscribeTicker(pair goex.CurrencyPair) error { + defer func() { + s.reqId++ + }() + + return s.c.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{pair.ToLower().ToSymbol("") + "@ticker"}, + Id: s.reqId, + }) +} + +func (s *SpotWs) SubscribeTrade(pair goex.CurrencyPair) error { + panic("implement me") +} + +func (s *SpotWs) handle(data []byte) error { + var r resp + err := json2.Unmarshal(data, &r) + if err != nil { + logger.Errorf("json unmarshal ws response error [%s] , response data = %s", err, string(data)) + return err + } + + if strings.HasSuffix(r.Stream, "@depth10@100ms") { + return s.depthHandle(r.Data, adaptStreamToCurrencyPair(r.Stream)) + } + + if strings.HasSuffix(r.Stream, "@ticker") { + return s.tickerHandle(r.Data, adaptStreamToCurrencyPair(r.Stream)) + } + + logger.Warn("unknown ws response:", string(data)) + + return nil +} + +func (s *SpotWs) depthHandle(data json2.RawMessage, pair goex.CurrencyPair) error { + var ( + depthR depthResp + dep goex.Depth + err error + ) + + err = json2.Unmarshal(data, &depthR) + if err != nil { + logger.Errorf("unmarshal depth response error %s[] , response data = %s", err, string(data)) + return err + } + + dep.UTime = time.Now() + dep.Pair = pair + + for _, bid := range depthR.Bids { + dep.BidList = append(dep.BidList, goex.DepthRecord{ + Price: goex.ToFloat64(bid[0]), + Amount: goex.ToFloat64(bid[1]), + }) + } + + for _, ask := range depthR.Asks { + dep.AskList = append(dep.AskList, goex.DepthRecord{ + Price: goex.ToFloat64(ask[0]), + Amount: goex.ToFloat64(ask[1]), + }) + } + + sort.Sort(sort.Reverse(dep.AskList)) + + s.depthCallFn(&dep) + + return nil +} + +func (s *SpotWs) tickerHandle(data json2.RawMessage, pair goex.CurrencyPair) error { + var ( + tickerData = make(map[string]interface{}, 4) + ticker goex.Ticker + ) + + err := json2.Unmarshal(data, &tickerData) + if err != nil { + logger.Errorf("unmarshal ticker response data error [%s] , data = %s", err, string(data)) + return err + } + + ticker.Pair = pair + ticker.Vol = goex.ToFloat64(tickerData["v"]) + ticker.Last = goex.ToFloat64(tickerData["c"]) + ticker.Sell = goex.ToFloat64(tickerData["a"]) + ticker.Buy = goex.ToFloat64(tickerData["b"]) + ticker.High = goex.ToFloat64(tickerData["h"]) + ticker.Low = goex.ToFloat64(tickerData["l"]) + ticker.Date = goex.ToUint64(tickerData["E"]) + + s.tickerCallFn(&ticker) + + return nil +} diff --git a/binance/SpotWs_test.go b/binance/SpotWs_test.go new file mode 100644 index 00000000..dbc3b254 --- /dev/null +++ b/binance/SpotWs_test.go @@ -0,0 +1,32 @@ +package binance + +import ( + "github.com/nntaoli-project/goex" + "log" + "os" + "testing" + "time" +) + +var spotWs *SpotWs + +func init() { + os.Setenv("HTTPS_PROXY", "socks5://127.0.0.1:1080") + spotWs = NewSpotWs() + spotWs.DepthCallback(func(depth *goex.Depth) { + log.Println(depth) + }) + spotWs.TickerCallback(func(ticker *goex.Ticker) { + log.Println(ticker) + }) +} + +func TestSpotWs_DepthCallback(t *testing.T) { + spotWs.SubscribeDepth(goex.BTC_USDT) + time.Sleep(11 * time.Minute) +} + +func TestSpotWs_SubscribeTicker(t *testing.T) { + spotWs.SubscribeTicker(goex.LTC_USDT) + time.Sleep(30 * time.Minute) +} diff --git a/builder/APIBuilder.go b/builder/APIBuilder.go index b6aaed2c..b988364d 100644 --- a/builder/APIBuilder.go +++ b/builder/APIBuilder.go @@ -343,6 +343,8 @@ func (builder *APIBuilder) BuildSpotWs(exName string) (SpotWsApi, error) { return okex.NewOKExSpotV3Ws(nil), nil case HUOBI_PRO, HUOBI: return huobi.NewSpotWs(), nil + case BINANCE: + return binance.NewSpotWs(), nil } return nil, errors.New("not support the exchange " + exName) } diff --git a/websocket.go b/websocket.go index 9d674a96..492c1352 100644 --- a/websocket.go +++ b/websocket.go @@ -310,6 +310,7 @@ func (ws *WsConn) receiveMessage() { ws.c.SetPingHandler(func(ping string) error { Log.Debugf("[%s] received [ping] %s", ws.WsUrl, ping) + ws.SendPongMessage([]byte(ping)) ws.c.SetReadDeadline(time.Now().Add(ws.readDeadLineTime)) return nil }) From a1c08d7d954414194503b89f823505b916ea0f82 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Wed, 9 Dec 2020 18:10:15 +0800 Subject: [PATCH 03/34] [binance] futures and swap ws api --- binance/Adapter.go | 22 +++++ binance/FuturesWs.go | 179 ++++++++++++++++++++++++++++++++++++++ binance/FuturesWs_test.go | 45 ++++++++++ binance/SpotWs_test.go | 7 +- builder/APIBuilder.go | 3 +- websocket.go | 1 + 6 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 binance/FuturesWs.go create mode 100644 binance/FuturesWs_test.go diff --git a/binance/Adapter.go b/binance/Adapter.go index c9e457d5..ee491215 100644 --- a/binance/Adapter.go +++ b/binance/Adapter.go @@ -23,3 +23,25 @@ func adaptStreamToCurrencyPair(stream string) goex.CurrencyPair { return goex.UNKNOWN_PAIR } + +func adaptSymbolToCurrencyPair(symbol string) goex.CurrencyPair { + symbol = strings.ToUpper(symbol) + + if strings.HasSuffix(symbol, "USD") { + return goex.NewCurrencyPair2(fmt.Sprintf("%s_USD", strings.TrimSuffix(symbol, "USD"))) + } + + if strings.HasSuffix(symbol, "USDT") { + return goex.NewCurrencyPair2(fmt.Sprintf("%s_USDT", strings.TrimSuffix(symbol, "USDT"))) + } + + if strings.HasSuffix(symbol, "PAX") { + return goex.NewCurrencyPair2(fmt.Sprintf("%s_PAX", strings.TrimSuffix(symbol, "PAX"))) + } + + if strings.HasSuffix(symbol, "BTC") { + return goex.NewCurrencyPair2(fmt.Sprintf("%s_BTC", strings.TrimSuffix(symbol, "BTC"))) + } + + return goex.UNKNOWN_PAIR +} diff --git a/binance/FuturesWs.go b/binance/FuturesWs.go new file mode 100644 index 00000000..45a23089 --- /dev/null +++ b/binance/FuturesWs.go @@ -0,0 +1,179 @@ +package binance + +import ( + "errors" + "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "net/http" + "net/url" + "os" + "sort" + "strings" + "time" +) + +type FuturesWs struct { + base *BinanceFutures + f *goex.WsConn + d *goex.WsConn + + depthCallFn func(depth *goex.Depth) + tickerCallFn func(ticker *goex.FutureTicker) + tradeCalFn func(trade *goex.Trade, contract string) +} + +func NewFuturesWs() *FuturesWs { + futuresWs := new(FuturesWs) + + wsBuilder := goex.NewWsBuilder(). + ProxyUrl(os.Getenv("HTTPS_PROXY")). + ProtoHandleFunc(futuresWs.handle).AutoReconnect() + futuresWs.f = wsBuilder.WsUrl("wss://fstream.binance.com/ws").Build() + futuresWs.d = wsBuilder.WsUrl("wss://dstream.binance.com/ws").Build() + futuresWs.base = NewBinanceFutures(&goex.APIConfig{ + HttpClient: &http.Client{ + Transport: &http.Transport{ + Proxy: func(r *http.Request) (*url.URL, error) { + return url.Parse(os.Getenv("HTTPS_PROXY")) + }, + }, + Timeout: 10 * time.Second, + }, + }) + + return futuresWs +} + +func (s *FuturesWs) DepthCallback(f func(depth *goex.Depth)) { + s.depthCallFn = f +} + +func (s *FuturesWs) TickerCallback(f func(ticker *goex.FutureTicker)) { + s.tickerCallFn = f +} + +func (s *FuturesWs) TradeCallback(f func(trade *goex.Trade, contract string)) { + s.tradeCalFn = f +} + +func (s *FuturesWs) SubscribeDepth(pair goex.CurrencyPair, contractType string) error { + switch contractType { + case goex.SWAP_CONTRACT, goex.QUARTER_CONTRACT, goex.BI_QUARTER_CONTRACT: + sym, _ := s.base.adaptToSymbol(pair.AdaptUsdtToUsd(), contractType) + return s.d.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{strings.ToLower(sym) + "@depth10@100ms"}, + Id: 2, + }) + case goex.SWAP_USDT_CONTRACT: + return s.f.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{pair.AdaptUsdToUsdt().ToLower().ToSymbol("") + "@depth10@100ms"}, + Id: 1, + }) + } + return errors.New("contract is error") +} + +func (s *FuturesWs) SubscribeTicker(pair goex.CurrencyPair, contractType string) error { + switch contractType { + case goex.SWAP_CONTRACT, goex.QUARTER_CONTRACT, goex.BI_QUARTER_CONTRACT: + sym, _ := s.base.adaptToSymbol(pair.AdaptUsdtToUsd(), contractType) + return s.d.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{strings.ToLower(sym) + "@ticker"}, + Id: 2, + }) + case goex.SWAP_USDT_CONTRACT: + return s.f.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{pair.AdaptUsdToUsdt().ToLower().ToSymbol("") + "@ticker"}, + Id: 1, + }) + } + return errors.New("contract is error") +} + +func (s *FuturesWs) SubscribeTrade(pair goex.CurrencyPair, contractType string) error { + panic("implement me") +} + +func (s *FuturesWs) handle(data []byte) error { + var m = make(map[string]interface{}, 4) + err := json.Unmarshal(data, &m) + if err != nil { + return err + } + + if e, ok := m["e"].(string); ok && e == "depthUpdate" { + dep := s.depthHandle(m["b"].([]interface{}), m["a"].([]interface{})) + dep.ContractType = m["s"].(string) + symbol, ok := m["ps"].(string) + + if ok { + dep.Pair = adaptSymbolToCurrencyPair(symbol) + } else { + dep.Pair = adaptSymbolToCurrencyPair(dep.ContractType) //usdt swap + } + + dep.UTime = time.Unix(0, goex.ToInt64(m["T"])*int64(time.Millisecond)) + s.depthCallFn(dep) + + return nil + } + + if e, ok := m["e"].(string); ok && e == "24hrTicker" { + s.tickerCallFn(s.tickerHandle(m)) + return nil + } + + logger.Warn("unknown ws response:", string(data)) + + return nil +} + +func (s *FuturesWs) depthHandle(bids []interface{}, asks []interface{}) *goex.Depth { + var dep goex.Depth + + for _, item := range bids { + bid := item.([]interface{}) + dep.BidList = append(dep.BidList, + goex.DepthRecord{ + Price: goex.ToFloat64(bid[0]), + Amount: goex.ToFloat64(bid[1]), + }) + } + + for _, item := range asks { + ask := item.([]interface{}) + dep.AskList = append(dep.AskList, goex.DepthRecord{ + Price: goex.ToFloat64(ask[0]), + Amount: goex.ToFloat64(ask[1]), + }) + } + + sort.Sort(sort.Reverse(dep.AskList)) + + return &dep +} + +func (s *FuturesWs) tickerHandle(m map[string]interface{}) *goex.FutureTicker { + var ticker goex.FutureTicker + ticker.Ticker = new(goex.Ticker) + + symbol, ok := m["ps"].(string) + if ok { + ticker.Pair = adaptSymbolToCurrencyPair(symbol) + } else { + ticker.Pair = adaptSymbolToCurrencyPair(m["s"].(string)) //usdt swap + } + + ticker.ContractType = m["s"].(string) + ticker.Date = goex.ToUint64(m["E"]) + ticker.High = goex.ToFloat64(m["h"]) + ticker.Low = goex.ToFloat64(m["l"]) + ticker.Last = goex.ToFloat64(m["c"]) + ticker.Vol = goex.ToFloat64(m["v"]) + + return &ticker +} diff --git a/binance/FuturesWs_test.go b/binance/FuturesWs_test.go new file mode 100644 index 00000000..599908dd --- /dev/null +++ b/binance/FuturesWs_test.go @@ -0,0 +1,45 @@ +package binance + +import ( + "github.com/nntaoli-project/goex" + "log" + "os" + "testing" + "time" +) + +var futuresWs *FuturesWs + +func createFuturesWs() { + os.Setenv("HTTPS_PROXY", "socks5://127.0.0.1:1080") + + futuresWs = NewFuturesWs() + + futuresWs.DepthCallback(func(depth *goex.Depth) { + log.Println(depth) + }) + + futuresWs.TickerCallback(func(ticker *goex.FutureTicker) { + log.Println(ticker.Ticker, ticker.ContractType) + }) +} + +func TestFuturesWs_DepthCallback(t *testing.T) { + createFuturesWs() + + futuresWs.SubscribeDepth(goex.LTC_USDT, goex.SWAP_USDT_CONTRACT) + futuresWs.SubscribeDepth(goex.LTC_USDT, goex.SWAP_CONTRACT) + futuresWs.SubscribeDepth(goex.LTC_USDT, goex.QUARTER_CONTRACT) + + time.Sleep(30 * time.Second) +} + +func TestFuturesWs_SubscribeTicker(t *testing.T) { + createFuturesWs() + + futuresWs.SubscribeTicker(goex.BTC_USDT, goex.SWAP_USDT_CONTRACT) + futuresWs.SubscribeTicker(goex.BTC_USDT, goex.SWAP_CONTRACT) + futuresWs.SubscribeTicker(goex.BTC_USDT, goex.QUARTER_CONTRACT) + + time.Sleep(30 * time.Second) +} diff --git a/binance/SpotWs_test.go b/binance/SpotWs_test.go index dbc3b254..0cf92d8c 100644 --- a/binance/SpotWs_test.go +++ b/binance/SpotWs_test.go @@ -10,7 +10,7 @@ import ( var spotWs *SpotWs -func init() { +func createSpotWs() { os.Setenv("HTTPS_PROXY", "socks5://127.0.0.1:1080") spotWs = NewSpotWs() spotWs.DepthCallback(func(depth *goex.Depth) { @@ -22,11 +22,16 @@ func init() { } func TestSpotWs_DepthCallback(t *testing.T) { + createSpotWs() + spotWs.SubscribeDepth(goex.BTC_USDT) + spotWs.SubscribeTicker(goex.LTC_USDT) time.Sleep(11 * time.Minute) } func TestSpotWs_SubscribeTicker(t *testing.T) { + createSpotWs() + spotWs.SubscribeTicker(goex.LTC_USDT) time.Sleep(30 * time.Minute) } diff --git a/builder/APIBuilder.go b/builder/APIBuilder.go index b988364d..3afc633e 100644 --- a/builder/APIBuilder.go +++ b/builder/APIBuilder.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - . "github.com/nntaoli-project/goex" "github.com/nntaoli-project/goex/bigone" "github.com/nntaoli-project/goex/binance" @@ -333,6 +332,8 @@ func (builder *APIBuilder) BuildFuturesWs(exName string) (FuturesWsApi, error) { })), nil case HBDM: return huobi.NewHbdmWs(), nil + case BINANCE, BINANCE_FUTURES, BINANCE_SWAP: + return binance.NewFuturesWs(), nil } return nil, errors.New("not support the exchange " + exName) } diff --git a/websocket.go b/websocket.go index 492c1352..675f2806 100644 --- a/websocket.go +++ b/websocket.go @@ -264,6 +264,7 @@ func (ws *WsConn) Subscribe(subEvent interface{}) error { Log.Errorf("[ws][%s] json encode error , %s", ws.WsUrl, err) return err } + Log.Debug(string(data)) ws.writeBufferChan <- data ws.subs = append(ws.subs, data) return nil From 514c0d2c9d655c6c7eaeec1ba57429250adac7bb Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Wed, 9 Dec 2020 18:21:06 +0800 Subject: [PATCH 04/34] [binance] delete the ws old implement --- binance/BinanceWs.go | 402 -------------------------------------- binance/BinanceWs_test.go | 76 ------- 2 files changed, 478 deletions(-) delete mode 100644 binance/BinanceWs.go delete mode 100644 binance/BinanceWs_test.go diff --git a/binance/BinanceWs.go b/binance/BinanceWs.go deleted file mode 100644 index 4c3a5213..00000000 --- a/binance/BinanceWs.go +++ /dev/null @@ -1,402 +0,0 @@ -package binance - -import ( - "errors" - "fmt" - "github.com/json-iterator/go" - . "github.com/nntaoli-project/goex" - "strconv" - "strings" - "time" - "unsafe" -) - -var json = jsoniter.ConfigCompatibleWithStandardLibrary - -type BinanceWs struct { - baseURL string - combinedBaseURL string - proxyUrl string - tickerCallback func(*Ticker) - depthCallback func(*Depth) - tradeCallback func(*Trade) - klineCallback func(*Kline, int) - wsConns []*WsConn -} - -type AggTrade struct { - Trade - FirstBreakdownTradeID int64 `json:"f"` - LastBreakdownTradeID int64 `json:"l"` - TradeTime int64 `json:"T"` -} - -type RawTrade struct { - Trade - BuyerOrderID int64 `json:"b"` - SellerOrderID int64 `json:"a"` -} - -type DiffDepth struct { - Depth - UpdateID int64 `json:"u"` - FirstUpdateID int64 `json:"U"` -} - -var _INERNAL_KLINE_PERIOD_REVERTER = map[string]int{ - "1m": KLINE_PERIOD_1MIN, - "3m": KLINE_PERIOD_3MIN, - "5m": KLINE_PERIOD_5MIN, - "15m": KLINE_PERIOD_15MIN, - "30m": KLINE_PERIOD_30MIN, - "1h": KLINE_PERIOD_60MIN, - "2h": KLINE_PERIOD_2H, - "4h": KLINE_PERIOD_4H, - "6h": KLINE_PERIOD_6H, - "8h": KLINE_PERIOD_8H, - "12h": KLINE_PERIOD_12H, - "1d": KLINE_PERIOD_1DAY, - "3d": KLINE_PERIOD_3DAY, - "1w": KLINE_PERIOD_1WEEK, - "1M": KLINE_PERIOD_1MONTH, -} - -func NewBinanceWs() *BinanceWs { - bnWs := &BinanceWs{} - bnWs.baseURL = "wss://stream.binance.com:9443/ws" - bnWs.combinedBaseURL = "wss://stream.binance.com:9443/stream?streams=" - return bnWs -} - -func (bnWs *BinanceWs) ProxyUrl(proxyUrl string) { - bnWs.proxyUrl = proxyUrl -} - -func (bnWs *BinanceWs) SetBaseUrl(baseURL string) { - bnWs.baseURL = baseURL -} - -func (bnWs *BinanceWs) SetCombinedBaseURL(combinedBaseURL string) { - bnWs.combinedBaseURL = combinedBaseURL -} - -func (bnWs *BinanceWs) SetCallbacks( - tickerCallback func(*Ticker), - depthCallback func(*Depth), - tradeCallback func(*Trade), - klineCallback func(*Kline, int), -) { - bnWs.tickerCallback = tickerCallback - bnWs.depthCallback = depthCallback - bnWs.tradeCallback = tradeCallback - bnWs.klineCallback = klineCallback -} - -func (bnWs *BinanceWs) Subscribe(endpoint string, handle func(msg []byte) error) *WsConn { - wsConn := NewWsBuilder(). - WsUrl(endpoint). - AutoReconnect(). - ProtoHandleFunc(handle). - ProxyUrl(bnWs.proxyUrl). - ReconnectInterval(time.Millisecond * 5). - Build() - bnWs.wsConns = append(bnWs.wsConns, wsConn) - go bnWs.exitHandler(wsConn) - return wsConn -} - -func (bnWs *BinanceWs) Close() { - for _, con := range bnWs.wsConns { - con.CloseWs() - } -} - -func (bnWs *BinanceWs) SubscribeDepth(pair CurrencyPair, size int) error { - if bnWs.depthCallback == nil { - return errors.New("please set depth callback func") - } - if size != 5 && size != 10 && size != 20 { - return errors.New("please set depth size as 5 / 10 / 20") - } - endpoint := fmt.Sprintf("%s/%s@depth%d@100ms", bnWs.baseURL, strings.ToLower(pair.ToSymbol("")), size) - - handle := func(msg []byte) error { - rawDepth := struct { - LastUpdateID int64 `json:"lastUpdateId"` - Bids [][]interface{} `json:"bids"` - Asks [][]interface{} `json:"asks"` - }{} - err := json.Unmarshal(msg, &rawDepth) - if err != nil { - fmt.Println("json unmarshal error for ", string(msg)) - return err - } - depth := bnWs.parseDepthData(rawDepth.Bids, rawDepth.Asks) - depth.Pair = pair - depth.UTime = time.Now() - bnWs.depthCallback(depth) - return nil - } - bnWs.Subscribe(endpoint, handle) - return nil -} - -func (bnWs *BinanceWs) SubscribeTicker(pair CurrencyPair) error { - if bnWs.tickerCallback == nil { - return errors.New("please set ticker callback func") - } - endpoint := fmt.Sprintf("%s/%s@ticker", bnWs.baseURL, strings.ToLower(pair.ToSymbol(""))) - - handle := func(msg []byte) error { - datamap := make(map[string]interface{}) - err := json.Unmarshal(msg, &datamap) - if err != nil { - fmt.Println("json unmarshal error for ", string(msg)) - return err - } - - msgType, isOk := datamap["e"].(string) - if !isOk { - return errors.New("no message type") - } - - switch msgType { - case "24hrTicker": - tick := bnWs.parseTickerData(datamap) - tick.Pair = pair - bnWs.tickerCallback(tick) - return nil - default: - return errors.New("unknown message " + msgType) - } - } - bnWs.Subscribe(endpoint, handle) - return nil -} - -func (bnWs *BinanceWs) SubscribeTrade(pair CurrencyPair) error { - if bnWs.tradeCallback == nil { - return errors.New("please set trade callback func") - } - endpoint := fmt.Sprintf("%s/%s@trade", bnWs.baseURL, strings.ToLower(pair.ToSymbol(""))) - - handle := func(msg []byte) error { - datamap := make(map[string]interface{}) - err := json.Unmarshal(msg, &datamap) - if err != nil { - fmt.Println("json unmarshal error for ", string(msg)) - return err - } - - msgType, isOk := datamap["e"].(string) - if !isOk { - return errors.New("no message type") - } - - switch msgType { - case "trade": - side := BUY - if datamap["m"].(bool) == false { - side = SELL - } - trade := &RawTrade{ - Trade: Trade{ - Tid: int64(ToUint64(datamap["t"])), - Type: TradeSide(side), - Amount: ToFloat64(datamap["q"]), - Price: ToFloat64(datamap["p"]), - Date: int64(ToUint64(datamap["T"])), - }, - BuyerOrderID: ToInt64(datamap["b"]), - SellerOrderID: ToInt64(datamap["a"]), - } - trade.Pair = pair - bnWs.tradeCallback((*Trade)(unsafe.Pointer(trade))) - return nil - default: - return errors.New("unknown message " + msgType) - } - } - bnWs.Subscribe(endpoint, handle) - return nil -} - -func (bnWs *BinanceWs) SubscribeKline(pair CurrencyPair, period int) error { - if bnWs.klineCallback == nil { - return errors.New("place set kline callback func") - } - periodS, isOk := _INERNAL_KLINE_PERIOD_CONVERTER[period] - if isOk != true { - periodS = "M1" - } - endpoint := fmt.Sprintf("%s/%s@kline_%s", bnWs.baseURL, strings.ToLower(pair.ToSymbol("")), periodS) - - handle := func(msg []byte) error { - datamap := make(map[string]interface{}) - err := json.Unmarshal(msg, &datamap) - if err != nil { - fmt.Println("json unmarshal error for ", string(msg)) - return err - } - - msgType, isOk := datamap["e"].(string) - if !isOk { - return errors.New("no message type") - } - - switch msgType { - case "kline": - k := datamap["k"].(map[string]interface{}) - period := _INERNAL_KLINE_PERIOD_REVERTER[k["i"].(string)] - kline := bnWs.parseKlineData(k) - kline.Pair = pair - bnWs.klineCallback(kline, period) - return nil - default: - return errors.New("unknown message " + msgType) - } - } - bnWs.Subscribe(endpoint, handle) - return nil -} - -func (bnWs *BinanceWs) parseTickerData(tickmap map[string]interface{}) *Ticker { - t := new(Ticker) - t.Date = ToUint64(tickmap["E"]) - t.Last = ToFloat64(tickmap["c"]) - t.Vol = ToFloat64(tickmap["v"]) - t.Low = ToFloat64(tickmap["l"]) - t.High = ToFloat64(tickmap["h"]) - t.Buy = ToFloat64(tickmap["b"]) - t.Sell = ToFloat64(tickmap["a"]) - - return t -} - -func (bnWs *BinanceWs) parseDepthData(bids, asks [][]interface{}) *Depth { - depth := new(Depth) - for _, v := range bids { - depth.BidList = append(depth.BidList, DepthRecord{ToFloat64(v[0]), ToFloat64(v[1])}) - } - - for _, v := range asks { - depth.AskList = append(depth.AskList, DepthRecord{ToFloat64(v[0]), ToFloat64(v[1])}) - } - return depth -} - -func (bnWs *BinanceWs) parseKlineData(k map[string]interface{}) *Kline { - kline := &Kline{ - Timestamp: int64(ToInt(k["t"])) / 1000, - Open: ToFloat64(k["o"]), - Close: ToFloat64(k["c"]), - High: ToFloat64(k["h"]), - Low: ToFloat64(k["l"]), - Vol: ToFloat64(k["v"]), - } - return kline -} - -func (bnWs *BinanceWs) SubscribeAggTrade(pair CurrencyPair, tradeCallback func(*Trade)) error { - if tradeCallback == nil { - return errors.New("please set trade callback func") - } - endpoint := fmt.Sprintf("%s/%s@aggTrade", bnWs.baseURL, strings.ToLower(pair.ToSymbol(""))) - - handle := func(msg []byte) error { - datamap := make(map[string]interface{}) - err := json.Unmarshal(msg, &datamap) - if err != nil { - fmt.Println("json unmarshal error for ", string(msg)) - return err - } - - msgType, isOk := datamap["e"].(string) - if !isOk { - return errors.New("no message type") - } - - switch msgType { - case "aggTrade": - side := BUY - if datamap["m"].(bool) == false { - side = SELL - } - aggTrade := &AggTrade{ - Trade: Trade{ - Tid: int64(ToUint64(datamap["a"])), - Type: TradeSide(side), - Amount: ToFloat64(datamap["q"]), - Price: ToFloat64(datamap["p"]), - Date: int64(ToUint64(datamap["E"])), - }, - FirstBreakdownTradeID: int64(ToUint64(datamap["f"])), - LastBreakdownTradeID: int64(ToUint64(datamap["l"])), - TradeTime: int64(ToUint64(datamap["T"])), - } - aggTrade.Pair = pair - tradeCallback((*Trade)(unsafe.Pointer(aggTrade))) - return nil - default: - return errors.New("unknown message " + msgType) - } - } - bnWs.Subscribe(endpoint, handle) - return nil -} - -func (bnWs *BinanceWs) SubscribeDiffDepth(pair CurrencyPair, depthCallback func(*Depth)) error { - if depthCallback == nil { - return errors.New("please set depth callback func") - } - endpoint := fmt.Sprintf("%s/%s@depth", bnWs.baseURL, strings.ToLower(pair.ToSymbol(""))) - - handle := func(msg []byte) error { - rawDepth := struct { - Type string `json:"e"` - Time int64 `json:"E"` - Symbol string `json:"s"` - UpdateID int `json:"u"` - Bids [][]interface{} `json:"b"` - Asks [][]interface{} `json:"a"` - }{} - - err := json.Unmarshal(msg, &rawDepth) - if err != nil { - fmt.Println("json unmarshal error for ", string(msg)) - return err - } - diffDepth := new(DiffDepth) - for _, v := range rawDepth.Bids { - diffDepth.BidList = append(diffDepth.BidList, DepthRecord{ToFloat64(v[0]), ToFloat64(v[1])}) - } - - for _, v := range rawDepth.Asks { - diffDepth.AskList = append(diffDepth.AskList, DepthRecord{ToFloat64(v[0]), ToFloat64(v[1])}) - } - - diffDepth.Pair = pair - diffDepth.UTime = time.Unix(0, rawDepth.Time*int64(time.Millisecond)) - depthCallback((*Depth)(unsafe.Pointer(diffDepth))) - return nil - } - bnWs.Subscribe(endpoint, handle) - return nil -} - -func (bnWs *BinanceWs) exitHandler(c *WsConn) { - pingTicker := time.NewTicker(10 * time.Minute) - pongTicker := time.NewTicker(time.Second) - defer pingTicker.Stop() - defer pongTicker.Stop() - defer c.CloseWs() - - for { - select { - case t := <-pingTicker.C: - c.SendPingMessage([]byte(strconv.Itoa(int(t.UnixNano() / int64(time.Millisecond))))) - case t := <-pongTicker.C: - c.SendPongMessage([]byte(strconv.Itoa(int(t.UnixNano() / int64(time.Millisecond))))) - } - } -} diff --git a/binance/BinanceWs_test.go b/binance/BinanceWs_test.go deleted file mode 100644 index b31d127b..00000000 --- a/binance/BinanceWs_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package binance - -import ( - "github.com/nntaoli-project/goex" - "log" - "testing" - "time" - "unsafe" -) - -var bnWs = NewBinanceWs() - -func init() { - bnWs.proxyUrl = "socks5://127.0.0.1:1080" - //bnWs.SetBaseUrl("wss://fstream.binancezh.com/ws") - //bnWs.SetCombinedBaseURL("wss://fstream.binancezh.com/stream?streams=") - bnWs.SetCallbacks(printfTicker, printfDepth, printfTrade, printfKline) -} - -func printfTicker(ticker *goex.Ticker) { - log.Println("ticker:", ticker) -} - -func printfDepth(depth *goex.Depth) { - log.Println("depth:", depth) -} - -func printfTrade(trade *goex.Trade) { - log.Println("trade:", trade) - log.Println("trade:", (*RawTrade)(unsafe.Pointer(trade))) -} - -func printfAggTrade(aggTrade *goex.Trade) { - log.Println("trade:", (*AggTrade)(unsafe.Pointer(aggTrade))) -} -func printfKline(kline *goex.Kline, period int) { - log.Println("kline:", kline) -} - -func TestBinanceWs_SubscribeTicker(t *testing.T) { - bnWs.SubscribeTicker(goex.BTC_USDT) - time.Sleep(time.Second * 5) -} - -func TestBinanceWs_GetDepthWithWs(t *testing.T) { - bnWs.SubscribeDepth(goex.BTC_USDT, 5) - time.Sleep(time.Second * 10) -} - -func TestBinanceWs_GetKLineWithWs(t *testing.T) { - return - bnWs.SubscribeKline(goex.BTC_USDT, goex.KLINE_PERIOD_1MIN) - time.Sleep(time.Second * 10) -} - -func TestBinanceWs_GetTradesWithWs(t *testing.T) { - bnWs.SubscribeTrade(goex.BTC_USDT) - time.Sleep(time.Second * 5) -} - -func TestBinanceWs_SubscribeAggTrade(t *testing.T) { - bnWs.SubscribeAggTrade(goex.BTC_USDT, printfAggTrade) - time.Sleep(time.Second * 5) -} - -func TestBinanceWs_SubscribeDiffDepth(t *testing.T) { - bnWs.SubscribeDiffDepth(goex.BTC_USDT, printfDepth) - time.Sleep(time.Second * 10) -} - -func TestBinanceWs_SubscribeDepth(t *testing.T) { - bnWs.SubscribeDepth(goex.BTC_USDT, 5) - bnWs.SubscribeDepth(goex.LTC_USDT, 5) - bnWs.SubscribeDepth(goex.ETC_USDT, 5) - time.Sleep(time.Second * 60) -} From bc1b586bb77f706acc9d781f1e8f1a144e591e1f Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Wed, 9 Dec 2020 18:40:02 +0800 Subject: [PATCH 05/34] [binance] fix miss import json/encode package bug --- binance/Binance.go | 1 + binance/BinanceFutures.go | 1 + binance/BinanceSwap.go | 4 ++-- binance/FuturesWs.go | 1 + binance/Wallet.go | 45 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/binance/Binance.go b/binance/Binance.go index 4d14d185..a6bcac32 100644 --- a/binance/Binance.go +++ b/binance/Binance.go @@ -1,6 +1,7 @@ package binance import ( + "encoding/json" "errors" "fmt" . "github.com/nntaoli-project/goex" diff --git a/binance/BinanceFutures.go b/binance/BinanceFutures.go index 9c361810..0f8d8eb3 100644 --- a/binance/BinanceFutures.go +++ b/binance/BinanceFutures.go @@ -1,6 +1,7 @@ package binance import ( + "encoding/json" "errors" "fmt" . "github.com/nntaoli-project/goex" diff --git a/binance/BinanceSwap.go b/binance/BinanceSwap.go index ef590498..01541546 100644 --- a/binance/BinanceSwap.go +++ b/binance/BinanceSwap.go @@ -1,15 +1,15 @@ package binance import ( + "encoding/json" "errors" "fmt" + . "github.com/nntaoli-project/goex" "net/url" "strconv" "strings" "sync" "time" - - . "github.com/nntaoli-project/goex" ) const ( diff --git a/binance/FuturesWs.go b/binance/FuturesWs.go index 45a23089..10afd35a 100644 --- a/binance/FuturesWs.go +++ b/binance/FuturesWs.go @@ -1,6 +1,7 @@ package binance import ( + "encoding/json" "errors" "github.com/nntaoli-project/goex" "github.com/nntaoli-project/goex/internal/logger" diff --git a/binance/Wallet.go b/binance/Wallet.go index 5d2c7cb5..a21eefa8 100644 --- a/binance/Wallet.go +++ b/binance/Wallet.go @@ -1,9 +1,11 @@ package binance import ( + "encoding/json" "errors" "fmt" . "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" "net/url" ) @@ -48,7 +50,7 @@ func (w *Wallet) Transfer(param TransferParameter) error { } w.ba.buildParamsSigned(&postParam) - + resp, err := HttpPostForm2(w.ba.httpClient, transferUrl, postParam, map[string]string{"X-MBX-APIKEY": w.ba.accessKey}) @@ -70,9 +72,46 @@ func (w *Wallet) Transfer(param TransferParameter) error { } func (w *Wallet) GetWithDrawHistory(currency *Currency) ([]DepositWithdrawHistory, error) { - return nil, errors.New("not implement") + //historyUrl := w.conf.Endpoint + "/wapi/v3/withdrawHistory.html" + historyUrl := w.conf.Endpoint + "/sapi/v1/accountSnapshot" + postParam := url.Values{} + postParam.Set("type", "SPOT") + w.ba.buildParamsSigned(&postParam) + + resp, err := HttpGet5(w.ba.httpClient, historyUrl+"?"+postParam.Encode(), + map[string]string{"X-MBX-APIKEY": w.ba.accessKey}) + + if err != nil { + return nil, err + } + logger.Debugf("response body: %s", string(resp)) + respmap := make(map[string]interface{}) + err = json.Unmarshal(resp, &respmap) + if err != nil { + return nil, err + } + + return nil, nil } func (w *Wallet) GetDepositHistory(currency *Currency) ([]DepositWithdrawHistory, error) { - return nil, errors.New("not implement") + historyUrl := w.conf.Endpoint + "/wapi/v3/depositHistory.html" + postParam := url.Values{} + postParam.Set("asset", currency.Symbol) + w.ba.buildParamsSigned(&postParam) + + resp, err := HttpGet5(w.ba.httpClient, historyUrl+"?"+postParam.Encode(), + map[string]string{"X-MBX-APIKEY": w.ba.accessKey}) + + if err != nil { + return nil, err + } + logger.Debugf("response body: %s", string(resp)) + respmap := make(map[string]interface{}) + err = json.Unmarshal(resp, &respmap) + if err != nil { + return nil, err + } + + return nil, nil } From e48aef91108d265e68629b6b831a7282c49f949c Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Thu, 10 Dec 2020 12:02:30 +0800 Subject: [PATCH 06/34] [binance] futures GetFutureUserinfo api --- binance/BinanceFutures.go | 3 ++- binance/BinanceSwap.go | 8 ++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/binance/BinanceFutures.go b/binance/BinanceFutures.go index 0f8d8eb3..4cde10da 100644 --- a/binance/BinanceFutures.go +++ b/binance/BinanceFutures.go @@ -26,6 +26,7 @@ type AccountResponse struct { Assets []struct { Asset string `json:"asset"` WalletBalance float64 `json:"walletBalance,string"` + MarginBalance float64 `json:"marginBalance,string"` UnrealizedProfit float64 `json:"unrealizedProfit,string"` MaintMargin float64 `json:"maintMargin,string"` } `json:"assets"` @@ -262,7 +263,7 @@ func (bs *BinanceFutures) GetFutureUserinfo(currencyPair ...CurrencyPair) (*Futu currency := NewCurrency(asset.Asset, "") futureAccounts.FutureSubAccounts[currency] = FutureSubAccount{ Currency: NewCurrency(asset.Asset, ""), - AccountRights: asset.WalletBalance, + AccountRights: asset.MarginBalance, KeepDeposit: asset.MaintMargin, ProfitReal: 0, ProfitUnreal: asset.UnrealizedProfit, diff --git a/binance/BinanceSwap.go b/binance/BinanceSwap.go index 01541546..dfc1b8f8 100644 --- a/binance/BinanceSwap.go +++ b/binance/BinanceSwap.go @@ -246,9 +246,6 @@ func (bs *BinanceSwap) GetFutureIndex(currencyPair CurrencyPair) (float64, error return ToFloat64(respmap["markPrice"]), nil } -/** - *全仓账户 - */ func (bs *BinanceSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureAccount, error) { acc, err := bs.f.GetFutureUserinfo(currencyPair...) if err != nil { @@ -273,10 +270,9 @@ func (bs *BinanceSwap) GetFutureUserinfo(currencyPair ...CurrencyPair) (*FutureA currency := NewCurrency(vv["asset"].(string), "").AdaptBccToBch() acc.FutureSubAccounts[currency] = FutureSubAccount{ Currency: currency, - AccountRights: ToFloat64(vv["walletBalance"]), - KeepDeposit: ToFloat64(vv["marginBalance"]), + AccountRights: ToFloat64(vv["marginBalance"]), + KeepDeposit: ToFloat64(vv["maintMargin"]), ProfitUnreal: ToFloat64(vv["unrealizedProfit"]), - RiskRate: ToFloat64(vv["unrealizedProfit"]), } } From 82bd16e56fde792f65dc82074d3caddb8eddb08d Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Fri, 11 Dec 2020 12:56:10 +0800 Subject: [PATCH 07/34] [binance] futures support to enter contract ID --- binance/BinanceFutures.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/binance/BinanceFutures.go b/binance/BinanceFutures.go index 4cde10da..03561c7c 100644 --- a/binance/BinanceFutures.go +++ b/binance/BinanceFutures.go @@ -622,6 +622,10 @@ func (bs *BinanceFutures) adaptToSymbol(pair CurrencyPair, contractType string) if info.ContractType == "NEXT_QUARTER" && contractType == BI_QUARTER_CONTRACT { return info.Symbol, nil } + + if info.Symbol == contractType { + return info.Symbol,nil + } } } From 5deb512b601d70e0afffb056ad34f4bb54ff2575 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Fri, 11 Dec 2020 12:57:13 +0800 Subject: [PATCH 08/34] [binance] futues ws improve --- binance/FuturesWs.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/binance/FuturesWs.go b/binance/FuturesWs.go index 10afd35a..41eef8ea 100644 --- a/binance/FuturesWs.go +++ b/binance/FuturesWs.go @@ -59,38 +59,38 @@ func (s *FuturesWs) TradeCallback(f func(trade *goex.Trade, contract string)) { func (s *FuturesWs) SubscribeDepth(pair goex.CurrencyPair, contractType string) error { switch contractType { - case goex.SWAP_CONTRACT, goex.QUARTER_CONTRACT, goex.BI_QUARTER_CONTRACT: - sym, _ := s.base.adaptToSymbol(pair.AdaptUsdtToUsd(), contractType) - return s.d.Subscribe(req{ - Method: "SUBSCRIBE", - Params: []string{strings.ToLower(sym) + "@depth10@100ms"}, - Id: 2, - }) case goex.SWAP_USDT_CONTRACT: return s.f.Subscribe(req{ Method: "SUBSCRIBE", Params: []string{pair.AdaptUsdToUsdt().ToLower().ToSymbol("") + "@depth10@100ms"}, Id: 1, }) + default: + sym, _ := s.base.adaptToSymbol(pair.AdaptUsdtToUsd(), contractType) + return s.d.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{strings.ToLower(sym) + "@depth10@100ms"}, + Id: 2, + }) } return errors.New("contract is error") } func (s *FuturesWs) SubscribeTicker(pair goex.CurrencyPair, contractType string) error { switch contractType { - case goex.SWAP_CONTRACT, goex.QUARTER_CONTRACT, goex.BI_QUARTER_CONTRACT: - sym, _ := s.base.adaptToSymbol(pair.AdaptUsdtToUsd(), contractType) - return s.d.Subscribe(req{ - Method: "SUBSCRIBE", - Params: []string{strings.ToLower(sym) + "@ticker"}, - Id: 2, - }) case goex.SWAP_USDT_CONTRACT: return s.f.Subscribe(req{ Method: "SUBSCRIBE", Params: []string{pair.AdaptUsdToUsdt().ToLower().ToSymbol("") + "@ticker"}, Id: 1, }) + default: + sym, _ := s.base.adaptToSymbol(pair.AdaptUsdtToUsd(), contractType) + return s.d.Subscribe(req{ + Method: "SUBSCRIBE", + Params: []string{strings.ToLower(sym) + "@ticker"}, + Id: 2, + }) } return errors.New("contract is error") } From df5c78520ddffc9595eab7cf5d3c55e5c18a837d Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Fri, 11 Dec 2020 13:41:34 +0800 Subject: [PATCH 09/34] [spot api] refactor the GetOrderHistorys --- API.go | 2 +- Models.go | 7 +++++ Utils.go | 9 +++++++ allcoin/allcoin.go | 2 +- atop/atop.go | 9 +++---- bigone/Bigone.go | 2 +- bigone/BigoneV3.go | 2 +- bigone/Bigone_test.go | 2 +- binance/Binance.go | 2 +- binance/Binance_test.go | 39 ++++++++++----------------- bitfinex/bitfinex.go | 2 +- bithumb/bithumb.go | 2 +- bitstamp/Bitstamp.go | 2 +- bittrex/bittrex.go | 2 +- coinbig/coinbig.go | 2 +- coinex/coinex.go | 2 +- exx/exx.go | 16 +++++------ gdax/gdax.go | 2 +- hitbtc/Hitbtc.go | 3 ++- hitbtc/Hitbtc_test.go | 2 +- huobi/HuobiPro.go | 6 ++--- huobi/HuobiPro_test.go | 5 ++-- kraken/Kraken.go | 2 +- kucoin/kucoin.go | 60 ++++++++++++++++++++++------------------- okex/OKEx.go | 4 +-- okex/OKExSpot.go | 13 +++++++-- okex/OKEx_test.go | 23 ++++++---------- poloniex/Poloniex.go | 2 +- zb/Zb.go | 2 +- 29 files changed, 120 insertions(+), 108 deletions(-) diff --git a/API.go b/API.go index 80009eba..66386975 100644 --- a/API.go +++ b/API.go @@ -10,7 +10,7 @@ type API interface { CancelOrder(orderId string, currency CurrencyPair) (bool, error) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) - GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) + GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) ([]Order, error) GetAccount() (*Account, error) GetTicker(currency CurrencyPair) (*Ticker, error) diff --git a/Models.go b/Models.go index de2a4920..441b29d9 100644 --- a/Models.go +++ b/Models.go @@ -261,3 +261,10 @@ type DepositWithdrawHistory struct { Status int `json:"status,string"` Timestamp time.Time `json:"timestamp"` } + +type OptionalParameter map[string]string + +func (optional OptionalParameter) Optional(name, value string) OptionalParameter { + optional[name] = value + return optional +} diff --git a/Utils.go b/Utils.go index 4d196637..2125e330 100644 --- a/Utils.go +++ b/Utils.go @@ -114,6 +114,15 @@ func ValuesToJson(v url.Values) ([]byte, error) { return json.Marshal(parammap) } +func MergeOptionalParameter(values *url.Values, opts ...OptionalParameter) url.Values { + for _, opt := range opts { + for k, v := range opt { + values.Set(k, v) + } + } + return *values +} + func GzipDecompress(data []byte) ([]byte, error) { r, err := gzip.NewReader(bytes.NewReader(data)) if err != nil { diff --git a/allcoin/allcoin.go b/allcoin/allcoin.go index b6440a7a..3c71ac15 100755 --- a/allcoin/allcoin.go +++ b/allcoin/allcoin.go @@ -454,7 +454,7 @@ func (ac *Allcoin) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, e panic("not implements") } -func (ac *Allcoin) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (ac *Allcoin) GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) ([]Order, error) { panic("not implements") } func (ba *Allcoin) adaptCurrencyPair(pair CurrencyPair) CurrencyPair { diff --git a/atop/atop.go b/atop/atop.go index 2c6c0c38..f4f1d51a 100644 --- a/atop/atop.go +++ b/atop/atop.go @@ -544,18 +544,17 @@ func (at *Atop) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, erro return trades, nil } -func (at *Atop) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (at *Atop) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { //panic("not support") pair := at.adaptCurrencyPair(currency) path := ApiBaseUrl + GetHistorys params := url.Values{} params.Set("market", pair.ToLower().String()) - //params.Set("type", "1") - //params.Set("status", "0") - params.Set("page", fmt.Sprint(currentPage)) - params.Set("pageSize", fmt.Sprint(pageSize)) + + MergeOptionalParameter(¶ms, optional...) at.buildPostForm(¶ms) + resp, err := HttpPostForm(at.httpClient, path, params) if err != nil { return nil, err diff --git a/bigone/Bigone.go b/bigone/Bigone.go index e0ca9920..4d183c5a 100644 --- a/bigone/Bigone.go +++ b/bigone/Bigone.go @@ -347,7 +347,7 @@ func (bo *Bigone) GetOneOrder(orderId string, currencyPair goex.CurrencyPair) (* func (bo *Bigone) GetUnfinishOrders(currencyPair goex.CurrencyPair) ([]goex.Order, error) { return bo.getOrdersList(currencyPair, -1, goex.ORDER_UNFINISH) } -func (bo *Bigone) GetOrderHistorys(currencyPair goex.CurrencyPair, currentPage, pageSize int) ([]goex.Order, error) { +func (bo *Bigone) GetOrderHistorys(currencyPair goex.CurrencyPair, opt ...goex.OptionalParameter) ([]goex.Order, error) { return bo.getOrdersList(currencyPair, -1, goex.ORDER_FINISH) } diff --git a/bigone/BigoneV3.go b/bigone/BigoneV3.go index ce4a946e..982c532a 100644 --- a/bigone/BigoneV3.go +++ b/bigone/BigoneV3.go @@ -355,7 +355,7 @@ func (bo *BigoneV3) GetOneOrder(orderId string, currencyPair goex.CurrencyPair) func (bo *BigoneV3) GetUnfinishOrders(currencyPair goex.CurrencyPair) ([]goex.Order, error) { return bo.getOrdersList(currencyPair, 200, goex.ORDER_UNFINISH) } -func (bo *BigoneV3) GetOrderHistorys(currencyPair goex.CurrencyPair, currentPage, pageSize int) ([]goex.Order, error) { +func (bo *BigoneV3) GetOrderHistorys(currencyPair goex.CurrencyPair, opt goex.OptionalParameter) ([]goex.Order, error) { return bo.getOrdersList(currencyPair, 200, goex.ORDER_FINISH) } diff --git a/bigone/Bigone_test.go b/bigone/Bigone_test.go index d0758457..9ea336ee 100644 --- a/bigone/Bigone_test.go +++ b/bigone/Bigone_test.go @@ -30,7 +30,7 @@ func TestBigone_GetUnfinishOrders(t *testing.T) { func TestBigone_GetOrderHistorys(t *testing.T) { return TCT_BTC := NewCurrencyPair2("TCT_BTC") - t.Log(bo.GetOrderHistorys(TCT_BTC, 1, 1)) + t.Log(bo.GetOrderHistorys(TCT_BTC)) } func TestBigone_LimitSell(t *testing.T) { return diff --git a/binance/Binance.go b/binance/Binance.go index a6bcac32..44401ac0 100644 --- a/binance/Binance.go +++ b/binance/Binance.go @@ -610,7 +610,7 @@ func (bn *Binance) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, e return trades, nil } -func (bn *Binance) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (bn *Binance) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { params := url.Values{} params.Set("symbol", currency.ToSymbol("")) diff --git a/binance/Binance_test.go b/binance/Binance_test.go index b4defdc6..29ab5e72 100644 --- a/binance/Binance_test.go +++ b/binance/Binance_test.go @@ -2,30 +2,15 @@ package binance import ( "github.com/nntaoli-project/goex" - "net" "net/http" - "net/url" "testing" "time" ) var ba = NewWithConfig( &goex.APIConfig{ - HttpClient: &http.Client{ - Transport: &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse("socks5://127.0.0.1:1080") - return nil, nil - }, - Dial: (&net.Dialer{ - Timeout: 10 * time.Second, - }).Dial, - }, - Timeout: 10 * time.Second, - }, - Endpoint: GLOBAL_API_BASE_URL, - ApiKey: "q6y6Gr7fF3jSJLncpfn2PmAA0xu4XRiRFHpFkyJy3d7K68WUxY0Gt8rrajCDUfbI", - ApiSecretKey: "AP8C2kh4RyISN3fpRCFMZJddf233XbPcYWQ1S7gBan3pGjCQg2JnyQFSJrIaNzRh", + HttpClient: http.DefaultClient, + Endpoint: GLOBAL_API_BASE_URL, }) func TestBinance_GetTicker(t *testing.T) { @@ -34,26 +19,30 @@ func TestBinance_GetTicker(t *testing.T) { } func TestBinance_LimitBuy(t *testing.T) { - order, err := ba.LimitBuy("0.005", "8000", goex.BTC_USDT) + order, err := ba.LimitBuy("3.43", "29.5", goex.BNB_USDT) t.Log(order, err) } func TestBinance_LimitSell(t *testing.T) { - order, err := ba.LimitSell("0.01", "0.1", goex.LTC_BTC) + order, err := ba.LimitSell("0.0562", "17860", goex.BTC_USDT) t.Log(order, err) } func TestBinance_CancelOrder(t *testing.T) { - t.Log(ba.CancelOrder("1156274704", goex.BTC_USDT)) + r, er := ba.CancelOrder("3848718241", goex.BTC_USDT) + if !r { + t.Log((er.(goex.ApiError)).ErrCode) + } } func TestBinance_GetOneOrder(t *testing.T) { - t.Log(ba.GetOneOrder("1156274704", goex.BTC_USDT)) + odr, err := ba.GetOneOrder("g", goex.BTC_USDT) + t.Log(err, odr) } func TestBinance_GetDepth(t *testing.T) { //return - dep, err := ba.GetDepth(5, goex.ETH_BTC) + dep, err := ba.GetDepth(5, goex.NewCurrencyPair2("BTC_USDT")) t.Log(err) if err == nil { t.Log(dep.AskList) @@ -63,11 +52,11 @@ func TestBinance_GetDepth(t *testing.T) { func TestBinance_GetAccount(t *testing.T) { account, err := ba.GetAccount() - t.Log(account, err) + t.Log(err, account) } func TestBinance_GetUnfinishOrders(t *testing.T) { - orders, err := ba.GetUnfinishOrders(goex.ETH_BTC) + orders, err := ba.GetUnfinishOrders(goex.NewCurrencyPair2("BTC_USDT")) t.Log(orders, err) } @@ -94,5 +83,5 @@ func TestBinance_SetTimeOffset(t *testing.T) { } func TestBinance_GetOrderHistorys(t *testing.T) { - t.Log(ba.GetOrderHistorys(goex.BTC_USDT, 1, 1)) + t.Log(ba.GetOrderHistorys(goex.BTC_USDT)) } diff --git a/bitfinex/bitfinex.go b/bitfinex/bitfinex.go index ce2025e5..13973884 100644 --- a/bitfinex/bitfinex.go +++ b/bitfinex/bitfinex.go @@ -309,7 +309,7 @@ func (bfx *Bitfinex) GetUnfinishOrders(currencyPair CurrencyPair) ([]Order, erro return orders, nil } -func (bfx *Bitfinex) GetOrderHistorys(currencyPair CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (bfx *Bitfinex) GetOrderHistorys(currencyPair CurrencyPair, optional ...OptionalParameter) ([]Order, error) { panic("not implement") } diff --git a/bithumb/bithumb.go b/bithumb/bithumb.go index 1b6f85c2..7b98e8af 100644 --- a/bithumb/bithumb.go +++ b/bithumb/bithumb.go @@ -199,7 +199,7 @@ func (bit *Bithumb) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { return orders, nil } -func (bit *Bithumb) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (bit *Bithumb) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { panic("not implement") } diff --git a/bitstamp/Bitstamp.go b/bitstamp/Bitstamp.go index 80b3a943..fe68c3fe 100644 --- a/bitstamp/Bitstamp.go +++ b/bitstamp/Bitstamp.go @@ -324,7 +324,7 @@ func (bitstamp *Bitstamp) GetUnfinishOrders(currency CurrencyPair) ([]Order, err return orders, nil } -func (bitstamp *Bitstamp) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (bitstamp *Bitstamp) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { panic("not implement") } diff --git a/bittrex/bittrex.go b/bittrex/bittrex.go index 71f51cf1..4a724318 100644 --- a/bittrex/bittrex.go +++ b/bittrex/bittrex.go @@ -40,7 +40,7 @@ func (bx *Bittrex) GetOneOrder(orderId string, currency CurrencyPair) (*Order, e func (bx *Bittrex) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { panic("not implement") } -func (bx *Bittrex) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (bx *Bittrex) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { panic("not implement") } func (bx *Bittrex) GetAccount() (*Account, error) { diff --git a/coinbig/coinbig.go b/coinbig/coinbig.go index 9feabc71..376258a8 100644 --- a/coinbig/coinbig.go +++ b/coinbig/coinbig.go @@ -344,7 +344,7 @@ func (cb *CoinBig) GetUnfinishOrders(currencyPair CurrencyPair) ([]Order, error) } return orders, nil } -func (cb *CoinBig) GetOrderHistorys(currencyPair CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (cb *CoinBig) GetOrderHistorys(currencyPair CurrencyPair, optional ...OptionalParameter) ([]Order, error) { return nil, nil } func (cb *CoinBig) GetDepth(size int, currencyPair CurrencyPair) (*Depth, error) { diff --git a/coinex/coinex.go b/coinex/coinex.go index 499b59ad..e3041d81 100644 --- a/coinex/coinex.go +++ b/coinex/coinex.go @@ -178,7 +178,7 @@ func (coinex *CoinEx) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) return coinex.GetPendingOrders(1, 100, currency) } -func (coinex *CoinEx) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (coinex *CoinEx) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { panic("not implement") } diff --git a/exx/exx.go b/exx/exx.go index 643c0c48..38a68a3f 100644 --- a/exx/exx.go +++ b/exx/exx.go @@ -15,7 +15,7 @@ import ( ) const ( - EXX = "EXX" + EXX = "EXX" API_BASE_URL = "https://api.exx.com/" MARKET_URL = "http://api.exx.com/data/v1/" TICKER_API = "ticker?currency=%s" @@ -46,7 +46,7 @@ func (exx *Exx) GetExchangeName() string { } func (exx *Exx) GetTicker(currency CurrencyPair) (*Ticker, error) { - symbol := currency.AdaptBchToBcc().AdaptUsdToUsdt().ToLower().ToSymbol("_") + symbol := currency.ToLower().ToSymbol("_") path := MARKET_URL + fmt.Sprintf(TICKER_API, symbol) resp, err := HttpGet(exx.httpClient, path) if err != nil { @@ -73,7 +73,7 @@ func (exx *Exx) GetTicker(currency CurrencyPair) (*Ticker, error) { } func (exx *Exx) GetDepth(size int, currency CurrencyPair) (*Depth, error) { - symbol := currency.AdaptBchToBcc().AdaptUsdToUsdt().ToSymbol("_") + symbol := currency.ToSymbol("_") resp, err := HttpGet(exx.httpClient, MARKET_URL+fmt.Sprintf(DEPTH_API, symbol)) if err != nil { return nil, err @@ -177,7 +177,7 @@ func (exx *Exx) GetAccount() (*Account, error) { } func (exx *Exx) placeOrder(amount, price string, currency CurrencyPair, tradeType int) (*Order, error) { - symbol := currency.AdaptBchToBcc().AdaptUsdToUsdt().ToSymbol("_") + symbol := currency.ToSymbol("_") params := url.Values{} params.Set("method", "order") params.Set("price", price) @@ -237,7 +237,7 @@ func (exx *Exx) LimitSell(amount, price string, currency CurrencyPair, opt ...Li func (exx *Exx) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { - symbol := currency.AdaptBchToBcc().AdaptUsdToUsdt().ToSymbol("_") + symbol := currency.ToSymbol("_") params := url.Values{} params.Set("method", "cancelOrder") params.Set("id", orderId) @@ -309,7 +309,7 @@ func parseOrder(order *Order, ordermap map[string]interface{}) { } func (exx *Exx) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error) { - symbol := currency.AdaptBchToBcc().AdaptUsdToUsdt().ToSymbol("_") + symbol := currency.ToSymbol("_") params := url.Values{} params.Set("method", "getOrder") params.Set("id", orderId) @@ -339,7 +339,7 @@ func (exx *Exx) GetOneOrder(orderId string, currency CurrencyPair) (*Order, erro func (exx *Exx) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { params := url.Values{} - symbol := currency.AdaptBchToBcc().AdaptUsdToUsdt().ToSymbol("_") + symbol := currency.ToSymbol("_") params.Set("method", "getUnfinishedOrdersIgnoreTradeType") params.Set("currency", symbol) params.Set("pageIndex", "1") @@ -379,7 +379,7 @@ func (exx *Exx) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { return orders, nil } -func (exx *Exx) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (exx *Exx) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { return nil, nil } diff --git a/gdax/gdax.go b/gdax/gdax.go index 462242f9..8e309672 100644 --- a/gdax/gdax.go +++ b/gdax/gdax.go @@ -45,7 +45,7 @@ func (g *Gdax) GetOneOrder(orderId string, currency CurrencyPair) (*Order, error func (g *Gdax) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { panic("not implement") } -func (g *Gdax) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (g *Gdax) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { panic("not implement") } func (g *Gdax) GetAccount() (*Account, error) { diff --git a/hitbtc/Hitbtc.go b/hitbtc/Hitbtc.go index 4c0c8d63..90d7f0b0 100644 --- a/hitbtc/Hitbtc.go +++ b/hitbtc/Hitbtc.go @@ -291,9 +291,10 @@ func (hitbtc *Hitbtc) GetUnfinishOrders(currency goex.CurrencyPair) ([]goex.Orde // TODO // https://api.hitbtc.com/#orders-history -func (hitbtc *Hitbtc) GetOrderHistorys(currency goex.CurrencyPair, currentPage, pageSize int) ([]goex.Order, error) { +func (hitbtc *Hitbtc) GetOrderHistorys(currency goex.CurrencyPair, optional ...goex.OptionalParameter) ([]goex.Order, error) { params := url.Values{} params.Set("symbol", currency.ToSymbol("")) + resp := []map[string]interface{}{} err := hitbtc.doRequest("GET", ORDER_URI+"?"+params.Encode(), &resp) if err != nil { diff --git a/hitbtc/Hitbtc_test.go b/hitbtc/Hitbtc_test.go index c66794af..e1e743cd 100644 --- a/hitbtc/Hitbtc_test.go +++ b/hitbtc/Hitbtc_test.go @@ -87,7 +87,7 @@ func TestGetOneOrder(t *testing.T) { } func TestGetOrders(t *testing.T) { - res, err := htb.GetOrderHistorys(YCC_BTC, 1, 10) + res, err := htb.GetOrderHistorys(YCC_BTC) require := require.New(t) require.Nil(err) t.Log(res) diff --git a/huobi/HuobiPro.go b/huobi/HuobiPro.go index 484d3de0..f0ba6b28 100644 --- a/huobi/HuobiPro.go +++ b/huobi/HuobiPro.go @@ -441,10 +441,10 @@ func (hbpro *HuoBiPro) CancelOrder(orderId string, currency CurrencyPair) (bool, return true, nil } -func (hbpro *HuoBiPro) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (hbpro *HuoBiPro) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { return hbpro.getOrders(queryOrdersParams{ - pair: currency, - size: pageSize, + pair: currency, + //size: pageSize, states: "partial-canceled,filled", direct: "next", }) diff --git a/huobi/HuobiPro_test.go b/huobi/HuobiPro_test.go index 0f7662e4..be38ba57 100644 --- a/huobi/HuobiPro_test.go +++ b/huobi/HuobiPro_test.go @@ -31,10 +31,11 @@ var ( ) // -var hbpro = NewHuoBiProSpot(httpProxyClient, apikey, secretkey) +var hbpro *HuoBiPro func init() { logger.Log.SetLevel(logger.DEBUG) +// hbpro = NewHuoBiProSpot(httpProxyClient, apikey, secretkey) } func TestHuobiPro_GetTicker(t *testing.T) { @@ -124,7 +125,7 @@ func TestHuobiPro_GetOneOrder(t *testing.T) { } func TestHuobiPro_GetOrderHistorys(t *testing.T) { - ords, err := hbpro.GetOrderHistorys(goex.NewCurrencyPair2("HT_USDT"), 1, 3) + ords, err := hbpro.GetOrderHistorys(goex.NewCurrencyPair2("HT_USDT")) t.Log(err) t.Log(ords) } diff --git a/kraken/Kraken.go b/kraken/Kraken.go index 34ed1b05..3f0d8335 100644 --- a/kraken/Kraken.go +++ b/kraken/Kraken.go @@ -178,7 +178,7 @@ func (k *Kraken) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { return orders, nil } -func (k *Kraken) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (k *Kraken) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { panic("") } diff --git a/kucoin/kucoin.go b/kucoin/kucoin.go index e9c726fd..789b5404 100644 --- a/kucoin/kucoin.go +++ b/kucoin/kucoin.go @@ -94,12 +94,12 @@ func (kc *KuCoin) GetTicker(currency CurrencyPair) (*Ticker, error) { func (kc *KuCoin) LimitBuy(amount, price string, currency CurrencyPair, opt ...LimitOrderOptionalParameter) (*Order, error) { clientID := GenerateOrderClientId(32) in := kucoin.CreateOrderModel{ - ClientOid: clientID, - Side: "buy", - Symbol: currency.ToSymbol("-"), - Type: "limit", - Price: price, - Size: amount, + ClientOid: clientID, + Side: "buy", + Symbol: currency.ToSymbol("-"), + Type: "limit", + Price: price, + Size: amount, } resp, err := kc.service.CreateOrder(&in) if err != nil { @@ -124,12 +124,12 @@ func (kc *KuCoin) LimitBuy(amount, price string, currency CurrencyPair, opt ...L func (kc *KuCoin) LimitSell(amount, price string, currency CurrencyPair, opt ...LimitOrderOptionalParameter) (*Order, error) { clientID := GenerateOrderClientId(32) in := kucoin.CreateOrderModel{ - ClientOid: clientID, - Side: "sell", - Symbol: currency.ToSymbol("-"), - Type: "limit", - Price: price, - Size: amount, + ClientOid: clientID, + Side: "sell", + Symbol: currency.ToSymbol("-"), + Type: "limit", + Price: price, + Size: amount, } resp, err := kc.service.CreateOrder(&in) if err != nil { @@ -154,12 +154,12 @@ func (kc *KuCoin) LimitSell(amount, price string, currency CurrencyPair, opt ... func (kc *KuCoin) MarketBuy(amount, price string, currency CurrencyPair) (*Order, error) { clientID := GenerateOrderClientId(32) in := kucoin.CreateOrderModel{ - ClientOid: clientID, - Side: "buy", - Symbol: currency.ToSymbol("-"), - Type: "market", - Price: price, - Size: amount, + ClientOid: clientID, + Side: "buy", + Symbol: currency.ToSymbol("-"), + Type: "market", + Price: price, + Size: amount, } resp, err := kc.service.CreateOrder(&in) @@ -185,12 +185,12 @@ func (kc *KuCoin) MarketBuy(amount, price string, currency CurrencyPair) (*Order func (kc *KuCoin) MarketSell(amount, price string, currency CurrencyPair) (*Order, error) { clientID := GenerateOrderClientId(32) in := kucoin.CreateOrderModel{ - ClientOid: clientID, - Side: "sell", - Symbol: currency.ToSymbol("-"), - Type: "market", - Price: price, - Size: amount, + ClientOid: clientID, + Side: "sell", + Symbol: currency.ToSymbol("-"), + Type: "market", + Price: price, + Size: amount, } resp, err := kc.service.CreateOrder(&in) if err != nil { @@ -314,15 +314,19 @@ func (kc *KuCoin) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { return orders, nil } -func (kc *KuCoin) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (kc *KuCoin) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { params := map[string]string{ "status": "done", "symbol": currency.ToSymbol("-"), } - pagination := kucoin.PaginationParam{ - CurrentPage: int64(currentPage), - PageSize: int64(pageSize), + + pagination := kucoin.PaginationParam{} + + if len(optional) > 0 { + pagination.CurrentPage = ToInt64(optional[0]["currentPage"]) + pagination.PageSize = ToInt64(optional[0]["pageSize"]) } + resp, err := kc.service.Orders(params, &pagination) if err != nil { log.Error("KuCoin GetOrderHistorys error:", err) diff --git a/okex/OKEx.go b/okex/OKEx.go index 4256ef6b..7a413868 100644 --- a/okex/OKEx.go +++ b/okex/OKEx.go @@ -159,8 +159,8 @@ func (ok *OKEx) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { return ok.OKExSpot.GetUnfinishOrders(currency) } -func (ok *OKEx) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { - return ok.OKExSpot.GetOrderHistorys(currency, currentPage, pageSize) +func (ok *OKEx) GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) ([]Order, error) { + return ok.OKExSpot.GetOrderHistorys(currency, opt...) } func (ok *OKEx) GetAccount() (*Account, error) { diff --git a/okex/OKExSpot.go b/okex/OKExSpot.go index 60d813e7..76c0d833 100644 --- a/okex/OKExSpot.go +++ b/okex/OKExSpot.go @@ -5,6 +5,7 @@ import ( "github.com/go-openapi/errors" . "github.com/nntaoli-project/goex" "github.com/nntaoli-project/goex/internal/logger" + "net/url" "sort" "strings" "time" @@ -324,8 +325,16 @@ func (ok *OKExSpot) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { return ords, nil } -func (ok *OKExSpot) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { - urlPath := fmt.Sprintf("/api/spot/v3/orders?instrument_id=%s&state=7", currency.AdaptUsdToUsdt().ToSymbol("-")) +func (ok *OKExSpot) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { + urlPath := "/api/spot/v3/orders" + + param := url.Values{} + param.Set("instrument_id", currency.AdaptUsdToUsdt().ToSymbol("-")) + param.Set("state", "7") + MergeOptionalParameter(¶m, optional...) + + urlPath += "?" + param.Encode() + var response []OrderResponse err := ok.OKEx.DoRequest("GET", urlPath, "", &response) if err != nil { diff --git a/okex/OKEx_test.go b/okex/OKEx_test.go index ffff25f3..a41f67fe 100644 --- a/okex/OKEx_test.go +++ b/okex/OKEx_test.go @@ -15,20 +15,8 @@ func init() { // var config2 = &goex.APIConfig{ - Endpoint: "https://www.okex.me", - //HttpClient: &http.Client{ - // Transport: &http.Transport{ - // Proxy: func(req *http.Request) (*url.URL, error) { - // return &url.URL{ - // Scheme: "socks5", - // Host: "127.0.0.1:1080"}, nil - // }, - // }, - //}, //需要代理的这样配置 - HttpClient: http.DefaultClient, - ApiKey: "", - ApiSecretKey: "", - ApiPassphrase: "", + Endpoint: "https://www.okex.me", + HttpClient: http.DefaultClient, } var okex = NewOKEx(config2) //线上请用APIBuilder构建 @@ -245,5 +233,10 @@ func TestOKExSpot_GetCurrenciesPrecision(t *testing.T) { } func TestOKExSpot_GetOrderHistorys(t *testing.T) { - t.Log(okex.OKExSpot.GetOrderHistorys(goex.BTC_USDT, 1, 10)) + orders, err := okex.OKExSpot.GetOrderHistorys(goex.NewCurrencyPair2("DASH_USDT")) + if err != nil { + t.Log(err) + t.FailNow() + } + t.Log(len(orders)) } diff --git a/poloniex/Poloniex.go b/poloniex/Poloniex.go index a2b7200f..1ad5e00a 100644 --- a/poloniex/Poloniex.go +++ b/poloniex/Poloniex.go @@ -336,7 +336,7 @@ func (poloniex *Poloniex) GetUnfinishOrders(currency CurrencyPair) ([]Order, err //log.Println(orders) return orders, nil } -func (Poloniex *Poloniex) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (Poloniex *Poloniex) GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) ([]Order, error) { return nil, nil } diff --git a/zb/Zb.go b/zb/Zb.go index 28de51ea..2bc41bc3 100644 --- a/zb/Zb.go +++ b/zb/Zb.go @@ -371,7 +371,7 @@ func (zb *Zb) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { return orders, nil } -func (zb *Zb) GetOrderHistorys(currency CurrencyPair, currentPage, pageSize int) ([]Order, error) { +func (zb *Zb) GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) ([]Order, error) { return nil, nil } From aa82613ac67a5704fb94728a0f613b45ae4a50cc Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Fri, 11 Dec 2020 14:18:58 +0800 Subject: [PATCH 10/34] [binance] adapt order --- binance/Adapter.go | 19 ++++++ binance/Binance.go | 125 +++++++++++----------------------------- binance/Binance_test.go | 7 ++- 3 files changed, 58 insertions(+), 93 deletions(-) diff --git a/binance/Adapter.go b/binance/Adapter.go index ee491215..fdb82cdd 100644 --- a/binance/Adapter.go +++ b/binance/Adapter.go @@ -45,3 +45,22 @@ func adaptSymbolToCurrencyPair(symbol string) goex.CurrencyPair { return goex.UNKNOWN_PAIR } + +func adaptOrderStatus(status string) goex.TradeStatus { + var tradeStatus goex.TradeStatus + switch status { + case "NEW": + tradeStatus = goex.ORDER_UNFINISH + case "FILLED": + tradeStatus = goex.ORDER_FINISH + case "PARTIALLY_FILLED": + tradeStatus = goex.ORDER_PART_FINISH + case "CANCELED": + tradeStatus = goex.ORDER_CANCEL + case "PENDING_CANCEL": + tradeStatus = goex.ORDER_CANCEL_ING + case "REJECTED": + tradeStatus = goex.ORDER_REJECT + } + return tradeStatus +} diff --git a/binance/Binance.go b/binance/Binance.go index 44401ac0..bef79dee 100644 --- a/binance/Binance.go +++ b/binance/Binance.go @@ -462,48 +462,9 @@ func (bn *Binance) GetOneOrder(orderId string, currencyPair CurrencyPair) (*Orde return nil, err } - status := respmap["status"].(string) - side := respmap["side"].(string) - - ord := Order{} - ord.Currency = currencyPair - ord.OrderID = ToInt(orderId) - ord.OrderID2 = orderId - ord.Cid, _ = respmap["clientOrderId"].(string) - ord.Type = respmap["type"].(string) - - if side == "SELL" { - ord.Side = SELL - } else { - ord.Side = BUY - } - - switch status { - case "NEW": - ord.Status = ORDER_UNFINISH - case "FILLED": - ord.Status = ORDER_FINISH - case "PARTIALLY_FILLED": - ord.Status = ORDER_PART_FINISH - case "CANCELED": - ord.Status = ORDER_CANCEL - case "PENDING_CANCEL": - ord.Status = ORDER_CANCEL_ING - case "REJECTED": - ord.Status = ORDER_REJECT - } - - ord.Amount = ToFloat64(respmap["origQty"].(string)) - ord.Price = ToFloat64(respmap["price"].(string)) - ord.DealAmount = ToFloat64(respmap["executedQty"]) - ord.OrderTime = ToInt(respmap["time"]) - - cummulativeQuoteQty := ToFloat64(respmap["cummulativeQuoteQty"]) - if cummulativeQuoteQty > 0 { - ord.AvgPrice = cummulativeQuoteQty / ord.DealAmount - } + order := bn.adaptOrder(currencyPair, respmap) - return &ord, nil + return &order, nil } func (bn *Binance) GetUnfinishOrders(currencyPair CurrencyPair) ([]Order, error) { @@ -521,22 +482,9 @@ func (bn *Binance) GetUnfinishOrders(currencyPair CurrencyPair) ([]Order, error) orders := make([]Order, 0) for _, v := range respmap { ord := v.(map[string]interface{}) - side := ord["side"].(string) - orderSide := SELL - if side == "BUY" { - orderSide = BUY - } - ordId := ToInt(ord["orderId"]) - orders = append(orders, Order{ - OrderID: ordId, - OrderID2: strconv.Itoa(ordId), - Currency: currencyPair, - Price: ToFloat64(ord["price"]), - Amount: ToFloat64(ord["origQty"]), - Side: TradeSide(orderSide), - Status: ORDER_UNFINISH, - OrderTime: ToInt(ord["time"])}) + orders = append(orders, bn.adaptOrder(currencyPair, ord)) } + return orders, nil } @@ -612,9 +560,10 @@ func (bn *Binance) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, e func (bn *Binance) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { params := url.Values{} - params.Set("symbol", currency.ToSymbol("")) - + params.Set("symbol", currency.AdaptUsdToUsdt().ToSymbol("")) + MergeOptionalParameter(¶ms, optional...) bn.buildParamsSigned(¶ms) + path := bn.apiV3 + "allOrders?" + params.Encode() respmap, err := HttpGet3(bn.httpClient, path, map[string]string{"X-MBX-APIKEY": bn.accessKey}) @@ -624,40 +573,10 @@ func (bn *Binance) GetOrderHistorys(currency CurrencyPair, optional ...OptionalP orders := make([]Order, 0) for _, v := range respmap { - ord := v.(map[string]interface{}) - side := ord["side"].(string) - orderSide := SELL - if side == "BUY" { - orderSide = BUY - } - ordId := ToInt(ord["orderId"]) - status := ord["status"].(string) - var tradeStatus TradeStatus - switch status { - case "NEW": - tradeStatus = ORDER_UNFINISH - case "FILLED": - tradeStatus = ORDER_FINISH - case "PARTIALLY_FILLED": - tradeStatus = ORDER_PART_FINISH - case "CANCELED": - tradeStatus = ORDER_CANCEL - case "PENDING_CANCEL": - tradeStatus = ORDER_CANCEL_ING - case "REJECTED": - tradeStatus = ORDER_REJECT - } - - orders = append(orders, Order{ - OrderID: ToInt(ord["orderId"]), - OrderID2: strconv.Itoa(ordId), - Currency: currency, - Price: ToFloat64(ord["price"]), - Amount: ToFloat64(ord["origQty"]), - Side: TradeSide(orderSide), - Status: tradeStatus, - OrderTime: ToInt(ord["time"])}) + orderMap := v.(map[string]interface{}) + orders = append(orders, bn.adaptOrder(currency, orderMap)) } + return orders, nil } @@ -726,3 +645,27 @@ func (bn *Binance) adaptError(err error) error { return err } + +func (bn *Binance) adaptOrder(currencyPair CurrencyPair, orderMap map[string]interface{}) Order { + side := orderMap["side"].(string) + + orderSide := SELL + if side == "BUY" { + orderSide = BUY + } + + return Order{ + OrderID: ToInt(orderMap["orderId"]), + OrderID2: fmt.Sprint(orderMap["orderId"]), + Cid: orderMap["clientOrderId"].(string), + Currency: currencyPair, + Price: ToFloat64(orderMap["price"]), + Amount: ToFloat64(orderMap["origQty"]), + DealAmount: ToFloat64(orderMap["executedQty"]), + AvgPrice: FloatToFixed(ToFloat64(orderMap["cummulativeQuoteQty"])/ToFloat64(orderMap["executedQty"]), 8), + Side: TradeSide(orderSide), + Status: adaptOrderStatus(orderMap["status"].(string)), + OrderTime: ToInt(orderMap["time"]), + FinishedTime: ToInt64(orderMap["updateTime"]), + } +} diff --git a/binance/Binance_test.go b/binance/Binance_test.go index 29ab5e72..684b1dd3 100644 --- a/binance/Binance_test.go +++ b/binance/Binance_test.go @@ -36,7 +36,7 @@ func TestBinance_CancelOrder(t *testing.T) { } func TestBinance_GetOneOrder(t *testing.T) { - odr, err := ba.GetOneOrder("g", goex.BTC_USDT) + odr, err := ba.GetOneOrder("3874087228", goex.BTC_USDT) t.Log(err, odr) } @@ -83,5 +83,8 @@ func TestBinance_SetTimeOffset(t *testing.T) { } func TestBinance_GetOrderHistorys(t *testing.T) { - t.Log(ba.GetOrderHistorys(goex.BTC_USDT)) + t.Log(ba.GetOrderHistorys(goex.BTC_USDT, + goex.OptionalParameter{}. + Optional("startTime", "1607656034333"). + Optional("limit", "5"))) } From 5fd0e1ce4503cee3e521739dba333f5951bdefb2 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Fri, 11 Dec 2020 14:50:25 +0800 Subject: [PATCH 11/34] [huobi spot] GetOrderHistorys api --- huobi/HuobiPro.go | 41 ++++++++++++++++------------------------- huobi/HuobiPro_test.go | 11 ++++++----- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/huobi/HuobiPro.go b/huobi/HuobiPro.go index f0ba6b28..8136be70 100644 --- a/huobi/HuobiPro.go +++ b/huobi/HuobiPro.go @@ -348,6 +348,7 @@ func (hbpro *HuoBiPro) MarketSell(amount, price string, currency CurrencyPair) ( func (hbpro *HuoBiPro) parseOrder(ordmap map[string]interface{}) Order { ord := Order{ + Cid: fmt.Sprint(ordmap["client-order-id"]), OrderID: ToInt(ordmap["id"]), OrderID2: fmt.Sprint(ToInt(ordmap["id"])), Amount: ToFloat64(ordmap["amount"]), @@ -410,12 +411,9 @@ func (hbpro *HuoBiPro) GetOneOrder(orderId string, currency CurrencyPair) (*Orde } func (hbpro *HuoBiPro) GetUnfinishOrders(currency CurrencyPair) ([]Order, error) { - return hbpro.getOrders(queryOrdersParams{ - pair: currency, - states: "pre-submitted,submitted,partial-filled", - size: 100, - //direct:"" - }) + return hbpro.getOrders(currency, OptionalParameter{}. + Optional("states", "pre-submitted,submitted,partial-filled"). + Optional("size", "100")) } func (hbpro *HuoBiPro) CancelOrder(orderId string, currency CurrencyPair) (bool, error) { @@ -442,12 +440,13 @@ func (hbpro *HuoBiPro) CancelOrder(orderId string, currency CurrencyPair) (bool, } func (hbpro *HuoBiPro) GetOrderHistorys(currency CurrencyPair, optional ...OptionalParameter) ([]Order, error) { - return hbpro.getOrders(queryOrdersParams{ - pair: currency, - //size: pageSize, - states: "partial-canceled,filled", - direct: "next", - }) + var optionals []OptionalParameter + optionals = append(optionals, OptionalParameter{}. + Optional("states", "canceled,partial-canceled,filled"). + Optional("size", "100"). + Optional("direct", "next")) + optionals = append(optionals, optional...) + return hbpro.getOrders(currency, optionals...) } type queryOrdersParams struct { @@ -461,20 +460,12 @@ type queryOrdersParams struct { pair CurrencyPair } -func (hbpro *HuoBiPro) getOrders(queryparams queryOrdersParams) ([]Order, error) { +func (hbpro *HuoBiPro) getOrders(pair CurrencyPair, optional ...OptionalParameter) ([]Order, error) { path := "/v1/order/orders" params := url.Values{} - params.Set("symbol", strings.ToLower(queryparams.pair.AdaptUsdToUsdt().ToSymbol(""))) - params.Set("states", queryparams.states) - - if queryparams.direct != "" { - params.Set("direct", queryparams.direct) - } - - if queryparams.size > 0 { - params.Set("size", fmt.Sprint(queryparams.size)) - } - + params.Set("symbol", strings.ToLower(pair.AdaptUsdToUsdt().ToSymbol(""))) + MergeOptionalParameter(¶ms, optional...) + Log.Info(params) hbpro.buildPostForm("GET", path, ¶ms) respmap, err := HttpGet(hbpro.httpClient, fmt.Sprintf("%s%s?%s", hbpro.baseUrl, path, params.Encode())) if err != nil { @@ -490,7 +481,7 @@ func (hbpro *HuoBiPro) getOrders(queryparams queryOrdersParams) ([]Order, error) for _, v := range datamap { ordmap := v.(map[string]interface{}) ord := hbpro.parseOrder(ordmap) - ord.Currency = queryparams.pair + ord.Currency = pair orders = append(orders, ord) } diff --git a/huobi/HuobiPro_test.go b/huobi/HuobiPro_test.go index be38ba57..0dc6dd7f 100644 --- a/huobi/HuobiPro_test.go +++ b/huobi/HuobiPro_test.go @@ -33,9 +33,9 @@ var ( // var hbpro *HuoBiPro -func init() { +func init() { logger.Log.SetLevel(logger.DEBUG) -// hbpro = NewHuoBiProSpot(httpProxyClient, apikey, secretkey) + hbpro = NewHuoBiProSpot(httpProxyClient, apikey, secretkey) } func TestHuobiPro_GetTicker(t *testing.T) { @@ -118,14 +118,15 @@ func TestHuobiPro_CancelOrder(t *testing.T) { } func TestHuobiPro_GetOneOrder(t *testing.T) { - return - ord, err := hbpro.GetOneOrder("1116237737", goex.LTC_BTC) + ord, err := hbpro.GetOneOrder("165062634284339", goex.BTC_USDT) assert.Nil(t, err) t.Log(ord) } func TestHuobiPro_GetOrderHistorys(t *testing.T) { - ords, err := hbpro.GetOrderHistorys(goex.NewCurrencyPair2("HT_USDT")) + ords, err := hbpro.GetOrderHistorys( + goex.NewCurrencyPair2("BTC_USDT"), + goex.OptionalParameter{}.Optional("start-date","2020-11-30")) t.Log(err) t.Log(ords) } From da2a521d02d355bf88ad6d4d098a36678304eee0 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 14 Dec 2020 11:32:59 +0800 Subject: [PATCH 12/34] [spot] optimize the GetKlineRecords api parameter --- API.go | 2 +- atop/atop.go | 6 +++--- bigone/Bigone.go | 2 +- binance/Binance.go | 9 +++------ binance/BinanceSwap.go | 2 +- binance/Binance_test.go | 15 ++++++++++----- bitfinex/bitfinex.go | 6 +++--- bitfinex/bitfinex_test.go | 2 +- bithumb/bithumb.go | 2 +- bitstamp/Bitstamp.go | 2 +- bittrex/bittrex.go | 2 +- coinex/coinex.go | 2 +- gdax/gdax.go | 2 +- hitbtc/Hitbtc.go | 2 +- huobi/HuobiPro.go | 4 ++-- kucoin/kucoin.go | 7 ++++--- kucoin/kucoin_test.go | 3 +-- okex/OKEx.go | 4 ++-- okex/OKExSpot.go | 12 ++++-------- poloniex/Poloniex.go | 12 ++++++------ zb/Zb.go | 2 +- 21 files changed, 49 insertions(+), 51 deletions(-) diff --git a/API.go b/API.go index 66386975..310d6082 100644 --- a/API.go +++ b/API.go @@ -15,7 +15,7 @@ type API interface { GetTicker(currency CurrencyPair) (*Ticker, error) GetDepth(size int, currency CurrencyPair) (*Depth, error) - GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) + GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) //非个人,整个交易所的交易记录 GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) diff --git a/atop/atop.go b/atop/atop.go index f4f1d51a..bd781b80 100644 --- a/atop/atop.go +++ b/atop/atop.go @@ -113,7 +113,7 @@ const ( Withdrawal = "/trade/api/v1/withdraw" ) -var KlinePeriodConverter = map[int]string{ +var KlinePeriodConverter = map[KlinePeriod]string{ KLINE_PERIOD_1MIN: "1min", KLINE_PERIOD_3MIN: "3min", KLINE_PERIOD_5MIN: "5min", @@ -472,13 +472,13 @@ func (at *Atop) GetUnfinishOrders(currencyPair CurrencyPair) ([]Order, error) { } //hao -func (at *Atop) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (at *Atop) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]Kline, error) { pair := at.adaptCurrencyPair(currency) params := url.Values{} params.Set("market", pair.ToLower().String()) //params.Set("type", "1min") //1min,5min,15min,30min,1hour,6hour,1day,7day,30day params.Set("type", KlinePeriodConverter[period]) //1min,5min,15min,30min,1hour,6hour,1day,7day,30day - params.Set("since", fmt.Sprintf("%d", size)) //The first time is 0, followed by the value of the response since + MergeOptionalParameter(¶ms, opt...) klineUrl := ApiBaseUrl + GetKLine + "?" + params.Encode() kLines, err := HttpGet(at.httpClient, klineUrl) diff --git a/bigone/Bigone.go b/bigone/Bigone.go index 4d183c5a..6b31342b 100644 --- a/bigone/Bigone.go +++ b/bigone/Bigone.go @@ -471,7 +471,7 @@ func (bo *Bigone) GetDepth(size int, currencyPair goex.CurrencyPair) (*goex.Dept return depth, nil } -func (bo *Bigone) GetKlineRecords(currency goex.CurrencyPair, period, size, since int) ([]goex.Kline, error) { +func (bo *Bigone) GetKlineRecords(currency goex.CurrencyPair, period goex.KlinePeriod, size int, opt ...goex.OptionalParameter) ([]goex.Kline, error) { panic("not implements") } diff --git a/binance/Binance.go b/binance/Binance.go index bef79dee..fb1b6fb1 100644 --- a/binance/Binance.go +++ b/binance/Binance.go @@ -30,7 +30,7 @@ const ( SERVER_TIME_URL = "time" ) -var _INERNAL_KLINE_PERIOD_CONVERTER = map[int]string{ +var _INERNAL_KLINE_PERIOD_CONVERTER = map[KlinePeriod]string{ KLINE_PERIOD_1MIN: "1m", KLINE_PERIOD_3MIN: "3m", KLINE_PERIOD_5MIN: "5m", @@ -488,15 +488,12 @@ func (bn *Binance) GetUnfinishOrders(currencyPair CurrencyPair) ([]Order, error) return orders, nil } -func (bn *Binance) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (bn *Binance) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { params := url.Values{} params.Set("symbol", currency.ToSymbol("")) params.Set("interval", _INERNAL_KLINE_PERIOD_CONVERTER[period]) - if since > 0 { - params.Set("startTime", strconv.Itoa(since)) - } - //params.Set("endTime", strconv.Itoa(int(time.Now().UnixNano()/1000000))) params.Set("limit", fmt.Sprintf("%d", size)) + MergeOptionalParameter(¶ms, optional...) klineUrl := bn.apiV3 + KLINE_URI + "?" + params.Encode() klines, err := HttpGet3(bn.httpClient, klineUrl, nil) diff --git a/binance/BinanceSwap.go b/binance/BinanceSwap.go index dfc1b8f8..b2304da3 100644 --- a/binance/BinanceSwap.go +++ b/binance/BinanceSwap.go @@ -766,7 +766,7 @@ func (bs *BinanceSwap) GetKlineRecords(contractType string, currency CurrencyPai currency2 := bs.adaptCurrencyPair(currency) params := url.Values{} params.Set("symbol", currency2.ToSymbol("")) - params.Set("interval", _INERNAL_KLINE_PERIOD_CONVERTER[period]) + params.Set("interval", _INERNAL_KLINE_PERIOD_CONVERTER[KlinePeriod(period)]) if since > 0 { params.Set("startTime", strconv.Itoa(since)) } diff --git a/binance/Binance_test.go b/binance/Binance_test.go index 684b1dd3..ce64a7c0 100644 --- a/binance/Binance_test.go +++ b/binance/Binance_test.go @@ -1,6 +1,7 @@ package binance import ( + "fmt" "github.com/nntaoli-project/goex" "net/http" "testing" @@ -10,7 +11,7 @@ import ( var ba = NewWithConfig( &goex.APIConfig{ HttpClient: http.DefaultClient, - Endpoint: GLOBAL_API_BASE_URL, + Endpoint: "https://api.binancezh.pro", }) func TestBinance_GetTicker(t *testing.T) { @@ -19,12 +20,12 @@ func TestBinance_GetTicker(t *testing.T) { } func TestBinance_LimitBuy(t *testing.T) { - order, err := ba.LimitBuy("3.43", "29.5", goex.BNB_USDT) + order, err := ba.LimitBuy("3", "68.5", goex.LTC_USDT) t.Log(order, err) } func TestBinance_LimitSell(t *testing.T) { - order, err := ba.LimitSell("0.0562", "17860", goex.BTC_USDT) + order, err := ba.LimitSell("1", "90", goex.LTC_USDT) t.Log(order, err) } @@ -61,8 +62,12 @@ func TestBinance_GetUnfinishOrders(t *testing.T) { } func TestBinance_GetKlineRecords(t *testing.T) { - before := time.Now().Add(-time.Hour).Unix() * 1000 - kline, _ := ba.GetKlineRecords(goex.ETH_BTC, goex.KLINE_PERIOD_5MIN, 100, int(before)) + startTime := time.Now().Add(-24*time.Hour).Unix() * 1000 + endTime := time.Now().Add(-5*time.Hour).Unix() * 1000 + + kline, _ := ba.GetKlineRecords(goex.ETH_BTC, goex.KLINE_PERIOD_5MIN, 100, + goex.OptionalParameter{}.Optional("startTime", fmt.Sprint(startTime)).Optional("endTime", fmt.Sprint(endTime))) + for _, k := range kline { tt := time.Unix(k.Timestamp, 0) t.Log(tt, k.Open, k.Close, k.High, k.Low, k.Vol) diff --git a/bitfinex/bitfinex.go b/bitfinex/bitfinex.go index 13973884..e5422e67 100644 --- a/bitfinex/bitfinex.go +++ b/bitfinex/bitfinex.go @@ -89,17 +89,17 @@ func (bfx *Bitfinex) GetDepth(size int, currencyPair CurrencyPair) (*Depth, erro return depth, nil } -func (bfx *Bitfinex) GetKlineRecords(currencyPair CurrencyPair, klinePeriod, size, since int) ([]Kline, error) { +func (bfx *Bitfinex) GetKlineRecords(currencyPair CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { symbol := convertPairToBitfinexSymbol("t", currencyPair) if size == 0 { size = 100 } - period, ok := klinePeriods[KlinePeriod(klinePeriod)] + periodStr, ok := klinePeriods[period] if !ok { return nil, fmt.Errorf("invalid period") } - apiURL := fmt.Sprintf("%s/candles/trade:%s:%s/hist?limit=%d", apiURLV2, period, symbol, size) + apiURL := fmt.Sprintf("%s/candles/trade:%s:%s/hist?limit=%d", apiURLV2, periodStr, symbol, size) respRaw, err := NewHttpRequest(bfx.httpClient, "GET", apiURL, "", nil) if err != nil { diff --git a/bitfinex/bitfinex_test.go b/bitfinex/bitfinex_test.go index 14b9154b..eac37abd 100644 --- a/bitfinex/bitfinex_test.go +++ b/bitfinex/bitfinex_test.go @@ -21,7 +21,7 @@ func TestBitfinex_GetDepth(t *testing.T) { } func TestBitfinex_GetKline(t *testing.T) { - kline, _ := bfx.GetKlineRecords(goex.BTC_USD, goex.KLINE_PERIOD_1MONTH, 10, 0) + kline, _ := bfx.GetKlineRecords(goex.BTC_USD, goex.KLINE_PERIOD_1MONTH, 10) for _, k := range kline { t.Log(k) } diff --git a/bithumb/bithumb.go b/bithumb/bithumb.go index 7b98e8af..72ac7cfe 100644 --- a/bithumb/bithumb.go +++ b/bithumb/bithumb.go @@ -335,7 +335,7 @@ func (bit *Bithumb) GetDepth(size int, currency CurrencyPair) (*Depth, error) { return dep, nil } -func (bit *Bithumb) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (bit *Bithumb) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]Kline, error) { panic("not implement") } diff --git a/bitstamp/Bitstamp.go b/bitstamp/Bitstamp.go index fe68c3fe..5a340f2d 100644 --- a/bitstamp/Bitstamp.go +++ b/bitstamp/Bitstamp.go @@ -388,7 +388,7 @@ func (bitstamp *Bitstamp) GetDepth(size int, currency CurrencyPair) (*Depth, err return dep, nil } -func (bitstamp *Bitstamp) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (bitstamp *Bitstamp) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { panic("not implement") } diff --git a/bittrex/bittrex.go b/bittrex/bittrex.go index 4a724318..5d2be31a 100644 --- a/bittrex/bittrex.go +++ b/bittrex/bittrex.go @@ -105,7 +105,7 @@ func (bx *Bittrex) GetDepth(size int, currency CurrencyPair) (*Depth, error) { return dep, nil } -func (bx *Bittrex) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (bx *Bittrex) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]Kline, error) { panic("not implement") } diff --git a/coinex/coinex.go b/coinex/coinex.go index e3041d81..3c0b6232 100644 --- a/coinex/coinex.go +++ b/coinex/coinex.go @@ -238,7 +238,7 @@ func (coinex *CoinEx) GetAccount() (*Account, error) { return acc, nil } -func (coinex *CoinEx) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (coinex *CoinEx) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]Kline, error) { panic("not implement") } diff --git a/gdax/gdax.go b/gdax/gdax.go index 8e309672..b01a147a 100644 --- a/gdax/gdax.go +++ b/gdax/gdax.go @@ -116,7 +116,7 @@ func (g *Gdax) GetDepth(size int, currency CurrencyPair) (*Depth, error) { return dep, nil } -func (g *Gdax) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (g *Gdax) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]Kline, error) { urlpath := fmt.Sprintf("%s/products/%s/candles", g.baseUrl, currency.AdaptUsdtToUsd().ToSymbol("-")) granularity := -1 switch period { diff --git a/hitbtc/Hitbtc.go b/hitbtc/Hitbtc.go index 90d7f0b0..d2430a6d 100644 --- a/hitbtc/Hitbtc.go +++ b/hitbtc/Hitbtc.go @@ -396,7 +396,7 @@ func (hitbtc *Hitbtc) GetDepth(size int, currency goex.CurrencyPair) (*goex.Dept return &goex.Depth{AskList: askList, BidList: bidList}, nil } -func (hitbtc *Hitbtc) GetKlineRecords(currency goex.CurrencyPair, period, size, since int) ([]goex.Kline, error) { +func (hitbtc *Hitbtc) GetKlineRecords(currency goex.CurrencyPair, period goex.KlinePeriod, size int, opt ...goex.OptionalParameter) ([]goex.Kline, error) { panic("not implement") } diff --git a/huobi/HuobiPro.go b/huobi/HuobiPro.go index 8136be70..5c450656 100644 --- a/huobi/HuobiPro.go +++ b/huobi/HuobiPro.go @@ -17,7 +17,7 @@ import ( var HBPOINT = NewCurrency("HBPOINT", "") -var _INERNAL_KLINE_PERIOD_CONVERTER = map[int]string{ +var _INERNAL_KLINE_PERIOD_CONVERTER = map[KlinePeriod]string{ KLINE_PERIOD_1MIN: "1min", KLINE_PERIOD_5MIN: "5min", KLINE_PERIOD_15MIN: "15min", @@ -559,7 +559,7 @@ func (hbpro *HuoBiPro) GetDepth(size int, currency CurrencyPair) (*Depth, error) } //倒序 -func (hbpro *HuoBiPro) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (hbpro *HuoBiPro) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { url := hbpro.baseUrl + "/market/history/kline?period=%s&size=%d&symbol=%s" symbol := strings.ToLower(currency.AdaptUsdToUsdt().ToSymbol("")) periodS, isOk := _INERNAL_KLINE_PERIOD_CONVERTER[period] diff --git a/kucoin/kucoin.go b/kucoin/kucoin.go index 789b5404..3c4d1190 100644 --- a/kucoin/kucoin.go +++ b/kucoin/kucoin.go @@ -46,7 +46,7 @@ type KuCoin struct { service *kucoin.ApiService } -var inernalKlinePeriodConverter = map[int]string{ +var inernalKlinePeriodConverter = map[KlinePeriod]string{ KLINE_PERIOD_1MIN: "1min", KLINE_PERIOD_3MIN: "3min", KLINE_PERIOD_5MIN: "5min", @@ -391,8 +391,9 @@ func (kc *KuCoin) GetDepth(size int, currency CurrencyPair) (*Depth, error) { return &depth, nil } -func (kc *KuCoin) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { - resp, err := kc.service.KLines(currency.ToSymbol("-"), inernalKlinePeriodConverter[period], int64(since), time.Now().UnixNano()/int64(time.Millisecond)) +func (kc *KuCoin) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { + resp, err := kc.service.KLines(currency.ToSymbol("-"), inernalKlinePeriodConverter[period], 0, 0) + if err != nil { log.Error("KuCoin GetKlineRecords error:", err) return nil, err diff --git a/kucoin/kucoin_test.go b/kucoin/kucoin_test.go index c538a706..8e64489d 100644 --- a/kucoin/kucoin_test.go +++ b/kucoin/kucoin_test.go @@ -3,7 +3,6 @@ package kucoin import ( "github.com/nntaoli-project/goex" "testing" - "time" ) var kc = New("", "", "") @@ -19,7 +18,7 @@ func TestKuCoin_GetDepth(t *testing.T) { } func TestKuCoin_GetKlineRecords(t *testing.T) { - kLines, _ := kc.GetKlineRecords(goex.BTC_USDT, goex.KLINE_PERIOD_1MIN, 10, int(time.Now().Unix()-3600)) + kLines, _ := kc.GetKlineRecords(goex.BTC_USDT, goex.KLINE_PERIOD_1MIN, 10) t.Log(kLines) } diff --git a/okex/OKEx.go b/okex/OKEx.go index 7a413868..194847b1 100644 --- a/okex/OKEx.go +++ b/okex/OKEx.go @@ -175,8 +175,8 @@ func (ok *OKEx) GetDepth(size int, currency CurrencyPair) (*Depth, error) { return ok.OKExSpot.GetDepth(size, currency) } -func (ok *OKEx) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { - return ok.OKExSpot.GetKlineRecords(currency, period, size, since) +func (ok *OKEx) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { + return ok.OKExSpot.GetKlineRecords(currency, period, size, optional...) } func (ok *OKEx) GetTrades(currencyPair CurrencyPair, since int64) ([]Trade, error) { diff --git a/okex/OKExSpot.go b/okex/OKExSpot.go index 76c0d833..d291503e 100644 --- a/okex/OKExSpot.go +++ b/okex/OKExSpot.go @@ -423,16 +423,12 @@ func (ok *OKExSpot) GetDepth(size int, currency CurrencyPair) (*Depth, error) { return dep, nil } -func (ok *OKExSpot) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (ok *OKExSpot) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { urlPath := "/api/spot/v3/instruments/%s/candles?granularity=%d" - if since > 0 { - sinceTime := time.Unix(int64(since), 0).UTC() - if since/int(time.Second) != 1 { //如果不为秒,转为秒 - sinceTime = time.Unix(int64(since)/int64(time.Second), 0).UTC() - } - urlPath += "&start=" + sinceTime.Format(time.RFC3339) - } + optParam := url.Values{} + MergeOptionalParameter(&optParam, optional...) + urlPath += "&" + optParam.Encode() granularity := 60 switch period { diff --git a/poloniex/Poloniex.go b/poloniex/Poloniex.go index 1ad5e00a..0581e464 100644 --- a/poloniex/Poloniex.go +++ b/poloniex/Poloniex.go @@ -115,7 +115,7 @@ func (poloniex *Poloniex) GetDepth(size int, currency CurrencyPair) (*Depth, err return &depth, nil } -func (Poloniex *Poloniex) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (poloniex *Poloniex) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { return nil, nil } @@ -336,7 +336,7 @@ func (poloniex *Poloniex) GetUnfinishOrders(currency CurrencyPair) ([]Order, err //log.Println(orders) return orders, nil } -func (Poloniex *Poloniex) GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) ([]Order, error) { +func (poloniex *Poloniex) GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) ([]Order, error) { return nil, nil } @@ -386,7 +386,7 @@ func (poloniex *Poloniex) GetAccount() (*Account, error) { return acc, nil } -func (p *Poloniex) Withdraw(amount string, currency Currency, fees, receiveAddr, safePwd string) (string, error) { +func (poloniex *Poloniex) Withdraw(amount string, currency Currency, fees, receiveAddr, safePwd string) (string, error) { if currency == BCC { currency = BCH } @@ -396,16 +396,16 @@ func (p *Poloniex) Withdraw(amount string, currency Currency, fees, receiveAddr, params.Add("amount", amount) params.Add("currency", strings.ToUpper(currency.String())) - sign, err := p.buildPostForm(¶ms) + sign, err := poloniex.buildPostForm(¶ms) if err != nil { return "", err } headers := map[string]string{ - "Key": p.accessKey, + "Key": poloniex.accessKey, "Sign": sign} - resp, err := HttpPostForm2(p.client, TRADE_API, params, headers) + resp, err := HttpPostForm2(poloniex.client, TRADE_API, params, headers) if err != nil { log.Println(err) diff --git a/zb/Zb.go b/zb/Zb.go index 2bc41bc3..0816a56c 100644 --- a/zb/Zb.go +++ b/zb/Zb.go @@ -375,7 +375,7 @@ func (zb *Zb) GetOrderHistorys(currency CurrencyPair, opt ...OptionalParameter) return nil, nil } -func (zb *Zb) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (zb *Zb) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]Kline, error) { return nil, nil } From b03a934a24c1e9a4f0149e6c9ac67d68fe20c5d2 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 14 Dec 2020 11:47:48 +0800 Subject: [PATCH 13/34] [models] OptionalParameter Type --- Models.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Models.go b/Models.go index 441b29d9..7e6ccea3 100644 --- a/Models.go +++ b/Models.go @@ -1,6 +1,7 @@ package goex import ( + "fmt" "net/http" "time" ) @@ -264,7 +265,7 @@ type DepositWithdrawHistory struct { type OptionalParameter map[string]string -func (optional OptionalParameter) Optional(name, value string) OptionalParameter { - optional[name] = value +func (optional OptionalParameter) Optional(name string, value interface{}) OptionalParameter { + optional[name] = fmt.Sprint(value) return optional } From 716191c399ec2c8c7cb8724484dcb5f6b45e04bc Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 14 Dec 2020 11:54:18 +0800 Subject: [PATCH 14/34] OptionalParameter Type --- Models.go | 31 +++++++++++++++++++++++++++++-- Utils.go | 2 +- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Models.go b/Models.go index 7e6ccea3..5e277d22 100644 --- a/Models.go +++ b/Models.go @@ -263,9 +263,36 @@ type DepositWithdrawHistory struct { Timestamp time.Time `json:"timestamp"` } -type OptionalParameter map[string]string +type OptionalParameter map[string]interface{} func (optional OptionalParameter) Optional(name string, value interface{}) OptionalParameter { - optional[name] = fmt.Sprint(value) + optional[name] = value return optional } + +func (optional OptionalParameter) GetString(name string) string { + return fmt.Sprint(optional[name]) +} + +func (optional OptionalParameter) GetInt(name string) int { + return ToInt(optional[name]) +} + +func (optional OptionalParameter) GetInt64(name string) int64 { + return ToInt64(optional[name]) +} + +func (optional OptionalParameter) GetFloat64(name string) float64 { + return ToFloat64(optional[name]) +} + +func (optional OptionalParameter) GetTime(name string) *time.Time { + val := optional["name"] + if val != nil { + t, ok := val.(time.Time) + if ok { + return &t + } + } + return nil +} diff --git a/Utils.go b/Utils.go index 2125e330..3a450d53 100644 --- a/Utils.go +++ b/Utils.go @@ -117,7 +117,7 @@ func ValuesToJson(v url.Values) ([]byte, error) { func MergeOptionalParameter(values *url.Values, opts ...OptionalParameter) url.Values { for _, opt := range opts { for k, v := range opt { - values.Set(k, v) + values.Set(k, fmt.Sprint(v)) } } return *values From 447a36146451b202325df53fc936c66efb2da398 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 14 Dec 2020 12:18:58 +0800 Subject: [PATCH 15/34] [futures] refactor the GetKlineRecords func --- FutureRestAPI.go | 2 +- binance/BinanceFutures.go | 4 ++-- binance/BinanceSwap.go | 8 +++----- bitmex/bitmex.go | 12 ++++++------ coinbene/CoinbeneSwap.go | 2 +- huobi/Hbdm.go | 4 ++-- huobi/Hbdm_Swap.go | 2 +- okex/OKExFuture.go | 15 +++------------ okex/OKExSwap.go | 11 ++--------- 9 files changed, 21 insertions(+), 39 deletions(-) diff --git a/FutureRestAPI.go b/FutureRestAPI.go index befa94dd..864529e2 100644 --- a/FutureRestAPI.go +++ b/FutureRestAPI.go @@ -111,7 +111,7 @@ type FutureRestAPI interface { /** * 获取K线数据 */ - GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) + GetKlineRecords(contractType string, currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]FutureKline, error) /** * 获取Trade数据 diff --git a/binance/BinanceFutures.go b/binance/BinanceFutures.go index 03561c7c..987e5b2c 100644 --- a/binance/BinanceFutures.go +++ b/binance/BinanceFutures.go @@ -560,7 +560,7 @@ func (bs *BinanceFutures) GetDeliveryTime() (int, int, int, int) { panic("not supported.") } -func (bs *BinanceFutures) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { +func (bs *BinanceFutures) GetKlineRecords(contractType string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) { panic("not supported.") } @@ -624,7 +624,7 @@ func (bs *BinanceFutures) adaptToSymbol(pair CurrencyPair, contractType string) } if info.Symbol == contractType { - return info.Symbol,nil + return info.Symbol, nil } } } diff --git a/binance/BinanceSwap.go b/binance/BinanceSwap.go index b2304da3..f0bb7ad1 100644 --- a/binance/BinanceSwap.go +++ b/binance/BinanceSwap.go @@ -754,9 +754,9 @@ func (bs *BinanceSwap) GetDeliveryTime() (int, int, int, int) { panic("not supported.") } -func (bs *BinanceSwap) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { +func (bs *BinanceSwap) GetKlineRecords(contractType string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) { if contractType == SWAP_CONTRACT { - return bs.f.GetKlineRecords(contractType, currency.AdaptUsdtToUsd(), period, since, since) + return bs.f.GetKlineRecords(contractType, currency.AdaptUsdtToUsd(), period, size, opt...) } if contractType != SWAP_USDT_CONTRACT { @@ -767,11 +767,9 @@ func (bs *BinanceSwap) GetKlineRecords(contractType string, currency CurrencyPai params := url.Values{} params.Set("symbol", currency2.ToSymbol("")) params.Set("interval", _INERNAL_KLINE_PERIOD_CONVERTER[KlinePeriod(period)]) - if since > 0 { - params.Set("startTime", strconv.Itoa(since)) - } //params.Set("endTime", strconv.Itoa(int(time.Now().UnixNano()/1000000))) params.Set("limit", strconv.Itoa(size)) + MergeOptionalParameter(¶ms, opt...) klineUrl := bs.apiV1 + KLINE_URI + "?" + params.Encode() klines, err := HttpGet3(bs.httpClient, klineUrl, nil) diff --git a/bitmex/bitmex.go b/bitmex/bitmex.go index b124d257..c497b38c 100644 --- a/bitmex/bitmex.go +++ b/bitmex/bitmex.go @@ -406,14 +406,9 @@ func (bm *bitmex) GetFutureEstimatedPrice(currencyPair CurrencyPair) (float64, e panic("no support") } -func (bm *bitmex) GetKlineRecords(contract_type string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { +func (bm *bitmex) GetKlineRecords(contract_type string, currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]FutureKline, error) { urlPath := "/api/v1/trade/bucketed?binSize=%s&partial=false&symbol=%s&count=%d&startTime=%s&reverse=true" contractId := bm.adaptCurrencyPairToSymbol(currency, contract_type) - sinceTime := time.Unix(int64(since), 0).UTC() - - if since/int(time.Second) != 1 { - sinceTime = time.Unix(int64(since)/int64(time.Second), 0).UTC() - } var granularity string switch period { @@ -429,6 +424,11 @@ func (bm *bitmex) GetKlineRecords(contract_type string, currency CurrencyPair, p granularity = "5m" } + sinceTime := time.Now() + if len(optional) > 0 && optional[0].GetTime("startTime") != nil { + sinceTime = *optional[0].GetTime("startTime") + } + uri := fmt.Sprintf(urlPath, granularity, contractId, size, sinceTime.Format(time.RFC3339)) response, err := HttpGet3(bm.HttpClient, bm.Endpoint+uri, nil) if err != nil { diff --git a/coinbene/CoinbeneSwap.go b/coinbene/CoinbeneSwap.go index 26602381..95c56a4c 100644 --- a/coinbene/CoinbeneSwap.go +++ b/coinbene/CoinbeneSwap.go @@ -351,7 +351,7 @@ func (swap *CoinbeneSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, con func (swap *CoinbeneSwap) GetFee() (float64, error) { panic("") } func (swap *CoinbeneSwap) GetContractValue(currencyPair CurrencyPair) (float64, error) { panic("") } func (swap *CoinbeneSwap) GetDeliveryTime() (int, int, int, int) { panic("") } -func (swap *CoinbeneSwap) GetKlineRecords(contract_type string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { +func (swap *CoinbeneSwap) GetKlineRecords(contract_type string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) { panic("") } func (swap *CoinbeneSwap) GetTrades(contract_type string, currencyPair CurrencyPair, since int64) ([]Trade, error) { diff --git a/huobi/Hbdm.go b/huobi/Hbdm.go index 50bd7143..34d3f62b 100644 --- a/huobi/Hbdm.go +++ b/huobi/Hbdm.go @@ -566,7 +566,7 @@ func (dm *Hbdm) GetFutureIndex(currencyPair CurrencyPair) (float64, error) { return ToFloat64(index), nil } -func (dm *Hbdm) GetKlineRecords(contract_type string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { +func (dm *Hbdm) GetKlineRecords(contract_type string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) { symbol := dm.adaptSymbol(currency, contract_type) periodS := dm.adaptKLinePeriod(period) url := fmt.Sprintf("%s/market/history/kline?symbol=%s&period=%s&size=%d", dm.config.Endpoint, symbol, periodS, size) @@ -640,7 +640,7 @@ func (dm *Hbdm) adaptSymbol(pair CurrencyPair, contractType string) string { return symbol } -func (dm *Hbdm) adaptKLinePeriod(period int) string { +func (dm *Hbdm) adaptKLinePeriod(period KlinePeriod) string { switch period { case KLINE_PERIOD_1MIN: return "1min" diff --git a/huobi/Hbdm_Swap.go b/huobi/Hbdm_Swap.go index 3c9df609..57758a55 100644 --- a/huobi/Hbdm_Swap.go +++ b/huobi/Hbdm_Swap.go @@ -426,7 +426,7 @@ func (swap *HbdmSwap) GetContractValue(currencyPair CurrencyPair) (float64, erro } } -func (swap *HbdmSwap) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { +func (swap *HbdmSwap) GetKlineRecords(contractType string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) { panic("not implement") } diff --git a/okex/OKExFuture.go b/okex/OKExFuture.go index 128bafa5..52218117 100644 --- a/okex/OKExFuture.go +++ b/okex/OKExFuture.go @@ -624,25 +624,16 @@ func (ok *OKExFuture) GetDeliveryTime() (int, int, int, int) { return 4, 16, 0, 0 //星期五,下午4点交割 } -/** - since : 单位秒,开始时间 -*/ -func (ok *OKExFuture) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { - urlPath := "/api/futures/v3/instruments/%s/candles?start=%s&granularity=%d" +func (ok *OKExFuture) GetKlineRecords(contractType string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) { + urlPath := "/api/futures/v3/instruments/%s/candles?granularity=%d" contractId := ok.GetFutureContractId(currency, contractType) - sinceTime := time.Unix(int64(since), 0).UTC() - - if since/int(time.Second) != 1 { //如果不为秒,转为秒 - sinceTime = time.Unix(int64(since)/int64(time.Second), 0).UTC() - } - granularity := adaptKLinePeriod(KlinePeriod(period)) if granularity == -1 { return nil, errors.New("kline period parameter is error") } var response [][]interface{} - err := ok.DoRequest("GET", fmt.Sprintf(urlPath, contractId, sinceTime.Format(time.RFC3339), granularity), "", &response) + err := ok.DoRequest("GET", fmt.Sprintf(urlPath, contractId, granularity), "", &response) if err != nil { return nil, err } diff --git a/okex/OKExSwap.go b/okex/OKExSwap.go index aeebe046..e709765a 100644 --- a/okex/OKExSwap.go +++ b/okex/OKExSwap.go @@ -537,19 +537,12 @@ func (ok *OKExSwap) GetDeliveryTime() (int, int, int, int) { panic("not support") } -func (ok *OKExSwap) GetKlineRecords(contractType string, currency CurrencyPair, period, size, since int) ([]FutureKline, error) { - - sinceTime := time.Unix(int64(since), 0).UTC() - - if since/int(time.Second) != 1 { //如果不为秒,转为秒 - sinceTime = time.Unix(int64(since)/int64(time.Second), 0).UTC() - } - +func (ok *OKExSwap) GetKlineRecords(contractType string, currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]FutureKline, error) { granularity := adaptKLinePeriod(KlinePeriod(period)) if granularity == -1 { return nil, errors.New("kline period parameter is error") } - return ok.GetKlineRecords2(contractType, currency, sinceTime.Format(time.RFC3339), "", strconv.Itoa(granularity)) + return ok.GetKlineRecords2(contractType, currency, "", "", strconv.Itoa(granularity)) } /** From 2d486c91fa0fd5e593612b79992a3a8610a16771 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 14 Dec 2020 12:19:52 +0800 Subject: [PATCH 16/34] [kraken] Supplementary submission adapt the GetKlineRecords --- kraken/Kraken.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/Kraken.go b/kraken/Kraken.go index 3f0d8335..d0db770c 100644 --- a/kraken/Kraken.go +++ b/kraken/Kraken.go @@ -270,7 +270,7 @@ func (k *Kraken) GetDepth(size int, currency CurrencyPair) (*Depth, error) { return &dep, nil } -func (k *Kraken) GetKlineRecords(currency CurrencyPair, period, size, since int) ([]Kline, error) { +func (k *Kraken) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, opt ...OptionalParameter) ([]Kline, error) { panic("") } From bc0e3b09b0bbfacc27ae8c05cbaf292fb696d3b9 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Tue, 15 Dec 2020 14:31:36 +0800 Subject: [PATCH 17/34] [bitmex] rename bitmex to Bitmex --- bitmex/{bitmex.go => Bitmex.go} | 1 + bitmex/{bitmex_test.go => Bitmex_test.go} | 9 ++++----- bitmex/SwapWs.go | 1 + bitmex/SwapWs_test.go | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) rename bitmex/{bitmex.go => Bitmex.go} (99%) rename bitmex/{bitmex_test.go => Bitmex_test.go} (92%) create mode 100644 bitmex/SwapWs.go create mode 100644 bitmex/SwapWs_test.go diff --git a/bitmex/bitmex.go b/bitmex/Bitmex.go similarity index 99% rename from bitmex/bitmex.go rename to bitmex/Bitmex.go index c497b38c..c98d0552 100644 --- a/bitmex/bitmex.go +++ b/bitmex/Bitmex.go @@ -309,6 +309,7 @@ func (bm *bitmex) GetFee() (float64, error) { func (bm *bitmex) GetFutureDepth(currencyPair CurrencyPair, contractType string, size int) (*Depth, error) { sym := bm.adaptCurrencyPairToSymbol(currencyPair, contractType) uri := fmt.Sprintf("/api/v1/orderBook/L2?symbol=%s&depth=%d", sym, size) + resp, err := HttpGet3(bm.HttpClient, bm.Endpoint+uri, nil) if err != nil { return nil, HTTP_ERR_CODE.OriginErr(err.Error()) diff --git a/bitmex/bitmex_test.go b/bitmex/Bitmex_test.go similarity index 92% rename from bitmex/bitmex_test.go rename to bitmex/Bitmex_test.go index 4f577438..e5f84a0a 100644 --- a/bitmex/bitmex_test.go +++ b/bitmex/Bitmex_test.go @@ -28,16 +28,15 @@ var httpProxyClient = &http.Client{ func init() { logger.Log.SetLevel(logger.DEBUG) mex = New(&goex.APIConfig{ - Endpoint: "https://testnet.bitmex.com/", - HttpClient: httpProxyClient, - ApiKey: "", - ApiSecretKey: ""}) + Endpoint: "https://testnet.bitmex.com/", + HttpClient: httpProxyClient, + }) } var mex *bitmex func TestBitmex_GetFutureDepth(t *testing.T) { - dep, err := mex.GetFutureDepth(goex.BTC_USD, "Z19", 5) + dep, err := mex.GetFutureDepth(goex.ETH_USDT, goex.SWAP_CONTRACT, 5) assert.Nil(t, err) t.Log(dep.AskList) t.Log(dep.BidList) diff --git a/bitmex/SwapWs.go b/bitmex/SwapWs.go new file mode 100644 index 00000000..38699ba8 --- /dev/null +++ b/bitmex/SwapWs.go @@ -0,0 +1 @@ +package bitmex diff --git a/bitmex/SwapWs_test.go b/bitmex/SwapWs_test.go new file mode 100644 index 00000000..38699ba8 --- /dev/null +++ b/bitmex/SwapWs_test.go @@ -0,0 +1 @@ +package bitmex From a49bb47fb45fcbcb7c6526c55c6cca46d27a2560 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Tue, 15 Dec 2020 14:32:29 +0800 Subject: [PATCH 18/34] [websocket] add the set EnableCompression value func --- websocket.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/websocket.go b/websocket.go index 675f2806..388e8006 100644 --- a/websocket.go +++ b/websocket.go @@ -25,6 +25,7 @@ type WsConfig struct { ErrorHandleFunc func(err error) ConnectSuccessAfterSendMessage func() []byte //for reconnect IsDump bool + EnableCompression bool readDeadLineTime time.Duration reconnectInterval time.Duration } @@ -99,6 +100,11 @@ func (b *WsBuilder) ProtoHandleFunc(f func([]byte) error) *WsBuilder { return b } +func (b *WsBuilder) EnableCompression(enable bool) *WsBuilder { + b.wsConfig.EnableCompression = enable + return b +} + func (b *WsBuilder) DecompressFunc(f func([]byte) ([]byte, error)) *WsBuilder { b.wsConfig.DecompressFunc = f return b @@ -159,6 +165,7 @@ func (ws *WsConn) connect() error { Log.Errorf("[ws][%s]parse proxy url [%s] err %s ", ws.WsUrl, ws.ProxyUrl, err.Error()) } } + dialer.EnableCompression = ws.EnableCompression wsConn, resp, err := dialer.Dial(ws.WsUrl, http.Header(ws.ReqHeaders)) if err != nil { From 8b6aa46acf273557cbc4f982926bdab059a50382 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Tue, 15 Dec 2020 16:19:46 +0800 Subject: [PATCH 19/34] [bitmes] ws implement --- bitmex/Adapter.go | 57 +++++++++++ bitmex/SwapWs.go | 216 ++++++++++++++++++++++++++++++++++++++++++ bitmex/SwapWs_test.go | 22 +++++ 3 files changed, 295 insertions(+) create mode 100644 bitmex/Adapter.go diff --git a/bitmex/Adapter.go b/bitmex/Adapter.go new file mode 100644 index 00000000..3c78efa4 --- /dev/null +++ b/bitmex/Adapter.go @@ -0,0 +1,57 @@ +package bitmex + +import ( + "fmt" + . "github.com/nntaoli-project/goex" + "strings" +) + +func AdaptCurrencyPairToSymbol(pair CurrencyPair, contract string) string { + if contract == "" || contract == SWAP_CONTRACT { + if pair.CurrencyA.Eq(BTC) { + pair = NewCurrencyPair(XBT, USD) + } + if pair.CurrencyB.Eq(BTC) { + pair = NewCurrencyPair(pair.CurrencyA, XBT) + } + return pair.AdaptUsdtToUsd().ToSymbol("") + } + + coin := pair.CurrencyA.Symbol + if pair.CurrencyA.Eq(BTC) { + coin = XBT.Symbol + } + return fmt.Sprintf("%s%s", coin, strings.ToUpper(contract)) +} + +func AdaptWsSymbol(symbol string) (pair CurrencyPair, contract string) { + symbol = strings.ToUpper(symbol) + + if symbol == "XBTCUSD" { + return BTC_USD, SWAP_CONTRACT + } + + if symbol == "BCHUSD" { + return BCH_USD, SWAP_CONTRACT + } + + if symbol == "ETHUSD" { + return ETH_USD, SWAP_CONTRACT + } + + if symbol == "LTCUSD" { + return LTC_USD, SWAP_CONTRACT + } + + if symbol == "LINKUSDT" { + return NewCurrencyPair2("LINK_USDT"), SWAP_CONTRACT + } + + pair = NewCurrencyPair(NewCurrency(symbol[0:3], ""), USDT) + contract = symbol[3:] + if pair.CurrencyA.Eq(XBT) { + return NewCurrencyPair(BTC, USDT), contract + } + + return pair, contract +} diff --git a/bitmex/SwapWs.go b/bitmex/SwapWs.go index 38699ba8..6b2a3bf5 100644 --- a/bitmex/SwapWs.go +++ b/bitmex/SwapWs.go @@ -1 +1,217 @@ package bitmex + +import ( + "encoding/json" + "fmt" + . "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "sort" + "time" +) + +type SubscribeOp struct { + Op string `json:"op"` + Args []string `json:"args"` +} + +type wsMessage struct { + Table string `json:"table"` + Action string `json:"action"` + Data json.RawMessage +} + +type tickerData struct { + Symbol string `json:"symbol"` + MakerFee float64 `json:"makerFee"` + TakerFee float64 `json:"takerFee"` + LastPrice float64 `json:"lastPrice"` + HighPrice float64 `json:"highPrice"` + LowPrice float64 `json:"lowPrice"` + AskPrice float64 `json:"askPrice"` + BidPrice float64 `json:"bidPrice"` + HomeNotional24h float64 `json:"homeNotional24h"` + Turnover24h float64 `json:"turnover24h"` + Timestamp string `json:"timestamp"` +} + +type depthData struct { + Symbol string `json:"symbol"` + Bids [][]interface{} `json:"bids"` + Asks [][]interface{} `json:"asks"` + Timestamp string `json:"timestamp"` +} + +type SwapWs struct { + c *WsConn + + depthCall func(depth *Depth) + tickerCall func(ticker *FutureTicker) + + tickerCacheMap map[string]FutureTicker +} + +func NewSwapWs() *SwapWs { + s := new(SwapWs) + wsBuilder := NewWsBuilder().EnableCompression(false).WsUrl("wss://www.bitmex.com/realtime") + wsBuilder = wsBuilder.Heartbeat(func() []byte { return []byte("ping") }, 5*time.Second) + wsBuilder = wsBuilder.ProtoHandleFunc(s.handle).AutoReconnect() + s.c = wsBuilder.Build() + s.tickerCacheMap = make(map[string]FutureTicker, 10) + return s +} + +func (s *SwapWs) DepthCallback(f func(depth *Depth)) { + s.depthCall = f +} + +func (s *SwapWs) TickerCallback(f func(ticker *FutureTicker)) { + s.tickerCall = f +} + +func (s *SwapWs) TradeCallback(f func(trade *Trade, contract string)) { + panic("implement me") +} + +func (s *SwapWs) SubscribeDepth(pair CurrencyPair, contractType string) error { + //{"op": "subscribe", "args": ["orderBook10:XBTUSD"]} + op := SubscribeOp{ + Op: "subscribe", + Args: []string{ + fmt.Sprintf("orderBook10:%s", AdaptCurrencyPairToSymbol(pair, contractType)), + }, + } + return s.c.Subscribe(op) +} + +func (s *SwapWs) SubscribeTicker(pair CurrencyPair, contractType string) error { + return s.c.Subscribe(SubscribeOp{ + Op: "subscribe", + Args: []string{ + "instrument:" + AdaptCurrencyPairToSymbol(pair, contractType), + }, + }) +} + +func (s *SwapWs) SubscribeTrade(pair CurrencyPair, contractType string) error { + panic("implement me") +} + +func (s *SwapWs) handle(data []byte) error { + if string(data) == "pong" { + return nil + } + + var msg wsMessage + err := json.Unmarshal(data, &msg) + if err != nil { + logger.Errorf("unmarshal error , message: %s", string(data)) + return err + } + + switch msg.Table { + case "orderBook10": + if msg.Action != "update" { + return nil + } + + var ( + depthData []depthData + dep Depth + ) + + err = json.Unmarshal(msg.Data, &depthData) + if err != nil { + logger.Errorf("unmarshal depth data error , data: %s", string(msg.Data)) + return nil + } + + if len(depthData) == 0 { + logger.Warn("depth data len==0 ??") + return nil + } + + dep.UTime, _ = time.Parse(time.RFC3339, depthData[0].Timestamp) + dep.Pair, dep.ContractType = AdaptWsSymbol(depthData[0].Symbol) + + for _, item := range depthData[0].Bids { + dep.BidList = append(dep.BidList, DepthRecord{ + Price: ToFloat64(item[0]), + Amount: ToFloat64(item[1]), + }) + } + + for _, item := range depthData[0].Asks { + dep.AskList = append(dep.AskList, DepthRecord{ + Price: ToFloat64(item[0]), + Amount: ToFloat64(item[1]), + }) + } + + sort.Sort(sort.Reverse(dep.AskList)) + + s.depthCall(&dep) + case "instrument": + var tickerData []tickerData + + err = json.Unmarshal(msg.Data, &tickerData) + if err != nil { + logger.Errorf("ticker data unmarshal error , data: %s", string(msg.Data)) + return err + } + + if msg.Action == "partial" { + ticker := s.tickerCacheMap[tickerData[0].Symbol] + ticker.Ticker = new(Ticker) + ticker.Pair, ticker.ContractType = AdaptWsSymbol(tickerData[0].Symbol) + ticker.Vol = tickerData[0].HomeNotional24h + ticker.Last = tickerData[0].LastPrice + ticker.Sell = tickerData[0].AskPrice + ticker.Buy = tickerData[0].BidPrice + ticker.High = tickerData[0].HighPrice + ticker.Low = tickerData[0].LowPrice + + tickerTime, _ := time.Parse(time.RFC3339, tickerData[0].Timestamp) + ticker.Date = uint64(tickerTime.Unix()) + + s.tickerCacheMap[tickerData[0].Symbol] = ticker + s.tickerCall(&ticker) + } + + if msg.Action == "update" { + ticker := s.tickerCacheMap[tickerData[0].Symbol] + tickerTime, _ := time.Parse(time.RFC3339, tickerData[0].Timestamp) + ticker.Date = uint64(tickerTime.Unix()) + + if tickerData[0].LastPrice > 0 { + ticker.Last = tickerData[0].LastPrice + } + + if tickerData[0].AskPrice > 0 { + ticker.Sell = tickerData[0].AskPrice + } + + if tickerData[0].BidPrice > 0 { + ticker.Buy = tickerData[0].BidPrice + } + + if tickerData[0].HighPrice > 0 { + ticker.High = tickerData[0].HighPrice + } + + if tickerData[0].LowPrice > 0 { + ticker.Low = tickerData[0].LowPrice + } + + if tickerData[0].HomeNotional24h > 0 { + ticker.Vol = tickerData[0].HomeNotional24h + } + + s.tickerCacheMap[tickerData[0].Symbol] = ticker + s.tickerCall(&ticker) + } + default: + logger.Warnf("unknown ws message: %s", string(data)) + } + + return nil +} diff --git a/bitmex/SwapWs_test.go b/bitmex/SwapWs_test.go index 38699ba8..6026309e 100644 --- a/bitmex/SwapWs_test.go +++ b/bitmex/SwapWs_test.go @@ -1 +1,23 @@ package bitmex + +import ( + "github.com/nntaoli-project/goex" + "os" + "testing" + "time" +) + +func TestNewSwapWs(t *testing.T) { + os.Setenv("HTTPS_PROXY", "socks5://127.0.0.1:1080") + ws := NewSwapWs() + ws.DepthCallback(func(depth *goex.Depth) { + t.Log(depth) + }) + ws.TickerCallback(func(ticker *goex.FutureTicker) { + t.Logf("%s %v", ticker.ContractType, ticker.Ticker) + }) + //ws.SubscribeDepth(goex.NewCurrencyPair2("LTC_USD"), goex.SWAP_CONTRACT) + ws.SubscribeTicker(goex.LTC_USDT , goex.SWAP_CONTRACT) + + time.Sleep(5 * time.Minute) +} From def99beade1fb1114f9b58c778268fe37cb33d54 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Tue, 15 Dec 2020 16:50:34 +0800 Subject: [PATCH 20/34] [websocket] DisableEnableCompression --- bitmex/SwapWs.go | 2 +- websocket.go | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bitmex/SwapWs.go b/bitmex/SwapWs.go index 6b2a3bf5..00a42ac2 100644 --- a/bitmex/SwapWs.go +++ b/bitmex/SwapWs.go @@ -52,7 +52,7 @@ type SwapWs struct { func NewSwapWs() *SwapWs { s := new(SwapWs) - wsBuilder := NewWsBuilder().EnableCompression(false).WsUrl("wss://www.bitmex.com/realtime") + wsBuilder := NewWsBuilder().DisableEnableCompression().WsUrl("wss://www.bitmex.com/realtime") wsBuilder = wsBuilder.Heartbeat(func() []byte { return []byte("ping") }, 5*time.Second) wsBuilder = wsBuilder.ProtoHandleFunc(s.handle).AutoReconnect() s.c = wsBuilder.Build() diff --git a/websocket.go b/websocket.go index 388e8006..9f1ade55 100644 --- a/websocket.go +++ b/websocket.go @@ -25,7 +25,7 @@ type WsConfig struct { ErrorHandleFunc func(err error) ConnectSuccessAfterSendMessage func() []byte //for reconnect IsDump bool - EnableCompression bool + DisableEnableCompression bool readDeadLineTime time.Duration reconnectInterval time.Duration } @@ -100,8 +100,8 @@ func (b *WsBuilder) ProtoHandleFunc(f func([]byte) error) *WsBuilder { return b } -func (b *WsBuilder) EnableCompression(enable bool) *WsBuilder { - b.wsConfig.EnableCompression = enable +func (b *WsBuilder) DisableEnableCompression() *WsBuilder { + b.wsConfig.DisableEnableCompression = true return b } @@ -165,7 +165,10 @@ func (ws *WsConn) connect() error { Log.Errorf("[ws][%s]parse proxy url [%s] err %s ", ws.WsUrl, ws.ProxyUrl, err.Error()) } } - dialer.EnableCompression = ws.EnableCompression + + if ws.DisableEnableCompression { + dialer.EnableCompression = false + } wsConn, resp, err := dialer.Dial(ws.WsUrl, http.Header(ws.ReqHeaders)) if err != nil { From 94cf9050da41dea773367457f7aab3c95f4cf350 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Tue, 15 Dec 2020 16:50:55 +0800 Subject: [PATCH 21/34] [APIBuilder] add bitmex swap ws --- builder/APIBuilder.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builder/APIBuilder.go b/builder/APIBuilder.go index 3afc633e..70de6597 100644 --- a/builder/APIBuilder.go +++ b/builder/APIBuilder.go @@ -316,7 +316,6 @@ func (builder *APIBuilder) BuildFuture(exName string) (api FutureRestAPI) { ApiKey: builder.apiKey, ApiSecretKey: builder.secretkey, }) - default: println(fmt.Sprintf("%s not support future", exName)) return nil @@ -334,6 +333,8 @@ func (builder *APIBuilder) BuildFuturesWs(exName string) (FuturesWsApi, error) { return huobi.NewHbdmWs(), nil case BINANCE, BINANCE_FUTURES, BINANCE_SWAP: return binance.NewFuturesWs(), nil + case BITMEX: + return bitmex.NewSwapWs(), nil } return nil, errors.New("not support the exchange " + exName) } From 18b63679e08a5d19a5f2f4e089382d6be01e4259 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Fri, 18 Dec 2020 11:33:24 +0800 Subject: [PATCH 22/34] [Futures Rest Api] Add The GetFutureOrderHistory API Implement --- FutureRestAPI.go | 5 +++++ binance/BinanceFutures.go | 4 ++++ binance/BinanceSwap.go | 4 ++++ bitmex/Bitmex.go | 4 ++++ coinbene/CoinbeneSwap.go | 4 ++++ huobi/Hbdm.go | 4 ++++ huobi/Hbdm_Swap.go | 4 ++++ okex/OKExFuture.go | 34 ++++++++++++++++++++++++++++++++++ okex/OKExSwap.go | 26 ++++++++++++++++++++++++++ 9 files changed, 89 insertions(+) diff --git a/FutureRestAPI.go b/FutureRestAPI.go index 864529e2..2b6c1a34 100644 --- a/FutureRestAPI.go +++ b/FutureRestAPI.go @@ -88,6 +88,11 @@ type FutureRestAPI interface { */ GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) + /** + * 获取个人订单历史,默认获取最近的订单历史列表,返回多少条订单数据,需根据平台接口定义而定 + */ + GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) + /** *获取交易费 */ diff --git a/binance/BinanceFutures.go b/binance/BinanceFutures.go index 987e5b2c..dabe4e6d 100644 --- a/binance/BinanceFutures.go +++ b/binance/BinanceFutures.go @@ -543,6 +543,10 @@ func (bs *BinanceFutures) GetUnfinishFutureOrders(currencyPair CurrencyPair, con return orders, nil } +func (bs *BinanceFutures) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + panic("implement me") +} + func (bs *BinanceFutures) GetFee() (float64, error) { panic("not supported.") } diff --git a/binance/BinanceSwap.go b/binance/BinanceSwap.go index f0bb7ad1..075e076a 100644 --- a/binance/BinanceSwap.go +++ b/binance/BinanceSwap.go @@ -194,6 +194,10 @@ func (bs *BinanceSwap) GetFutureDepth(currency CurrencyPair, contractType string return depth, nil } +func (bs *BinanceSwap) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + panic("implement me") +} + func (bs *BinanceSwap) GetTrades(contractType string, currencyPair CurrencyPair, since int64) ([]Trade, error) { if contractType == SWAP_CONTRACT { return bs.f.GetTrades(SWAP_CONTRACT, currencyPair.AdaptUsdtToUsd(), since) diff --git a/bitmex/Bitmex.go b/bitmex/Bitmex.go index c98d0552..a6e05bab 100644 --- a/bitmex/Bitmex.go +++ b/bitmex/Bitmex.go @@ -21,6 +21,10 @@ type bitmex struct { *APIConfig } +func (bm *bitmex) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + panic("implement me") +} + func New(config *APIConfig) *bitmex { bm := &bitmex{config} if bm.Endpoint == "" { diff --git a/coinbene/CoinbeneSwap.go b/coinbene/CoinbeneSwap.go index 95c56a4c..49e2d25b 100644 --- a/coinbene/CoinbeneSwap.go +++ b/coinbene/CoinbeneSwap.go @@ -38,6 +38,10 @@ type CoinbeneSwap struct { config APIConfig } +func (swap *CoinbeneSwap) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + panic("implement me") +} + func NewCoinbeneSwap(config APIConfig) *CoinbeneSwap { if config.Endpoint == "" { config.Endpoint = "http://openapi-contract.coinbene.com" diff --git a/huobi/Hbdm.go b/huobi/Hbdm.go index 34d3f62b..fc13a5ba 100644 --- a/huobi/Hbdm.go +++ b/huobi/Hbdm.go @@ -457,6 +457,10 @@ func (dm *Hbdm) GetFutureOrders(orderIds []string, currencyPair CurrencyPair, co } +func (dm *Hbdm) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + panic("implement me") +} + func (dm *Hbdm) GetContractValue(currencyPair CurrencyPair) (float64, error) { switch currencyPair.CurrencyA { case BTC: diff --git a/huobi/Hbdm_Swap.go b/huobi/Hbdm_Swap.go index 57758a55..428f2394 100644 --- a/huobi/Hbdm_Swap.go +++ b/huobi/Hbdm_Swap.go @@ -381,6 +381,10 @@ func (swap *HbdmSwap) GetFutureOrder(orderId string, currencyPair CurrencyPair, }, nil } +func (swap *HbdmSwap) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + panic("implement me") +} + func (swap *HbdmSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { param := url.Values{} param.Set("contract_code", currencyPair.ToSymbol("-")) diff --git a/okex/OKExFuture.go b/okex/OKExFuture.go index 52218117..188efc78 100644 --- a/okex/OKExFuture.go +++ b/okex/OKExFuture.go @@ -3,6 +3,7 @@ package okex import ( "errors" "fmt" + "net/url" "sort" "sync" "time" @@ -532,6 +533,39 @@ func (ok *OKExFuture) GetFutureOrders(orderIds []string, currencyPair CurrencyPa panic("") } +func (ok *OKExFuture) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + urlPath := fmt.Sprintf("/api/futures/v3/orders/%s?", ok.GetFutureContractId(pair, contractType)) + + param := url.Values{} + param.Set("limit", "100") + param.Set("state", "7") + MergeOptionalParameter(¶m, optional...) + urlPath += param.Encode() + + var response struct { + Result bool + OrderInfo []futureOrderResponse `json:"order_info"` + } + + err := ok.DoRequest("GET", urlPath, "", &response) + if err != nil { + return nil, err + } + + if !response.Result { + return nil, errors.New(fmt.Sprintf("%v", response)) + } + + orders := make([]FutureOrder, 0, 100) + for _, info := range response.OrderInfo { + ord := ok.adaptOrder(info) + ord.Currency = pair + orders = append(orders, ord) + } + + return orders, nil +} + type futureOrderResponse struct { InstrumentId string `json:"instrument_id"` ClientOid string `json:"client_oid"` diff --git a/okex/OKExSwap.go b/okex/OKExSwap.go index e709765a..3605380a 100644 --- a/okex/OKExSwap.go +++ b/okex/OKExSwap.go @@ -362,6 +362,32 @@ func (ok *OKExSwap) FutureCancelOrder(currencyPair CurrencyPair, contractType, o return resp.Result, nil } +func (ok *OKExSwap) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { + urlPath := fmt.Sprintf("/api/swap/v3/orders/%s?", ok.adaptContractType(pair)) + + param := url.Values{} + param.Set("limit", "100") + param.Set("state", "7") + MergeOptionalParameter(¶m, optional...) + + var response SwapOrdersInfo + + err := ok.DoRequest("GET", urlPath+param.Encode(), "", &response) + if err != nil { + return nil, err + } + + orders := make([]FutureOrder, 0, 100) + for _, info := range response.OrderInfo { + ord := ok.parseOrder(info) + ord.Currency = pair + ord.ContractName = contractType + orders = append(orders, ord) + } + + return orders, nil +} + func (ok *OKExSwap) parseOrder(ord BaseOrderInfo) FutureOrder { oTime, _ := time.Parse(time.RFC3339, ord.Timestamp) return FutureOrder{ From 54ca019321ab629211c90da49a306df8fd29dc8b Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Sun, 27 Dec 2020 16:11:36 +0800 Subject: [PATCH 23/34] [CurrencyPair] add currency pair const --- CurrencyPair.go | 67 ++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/CurrencyPair.go b/CurrencyPair.go index c4055085..e11a59cd 100644 --- a/CurrencyPair.go +++ b/CurrencyPair.go @@ -60,6 +60,10 @@ var ( TRX = Currency{"TRX", ""} GBP = Currency{"GBP", ""} XLM = Currency{"XLM", ""} + DOT = Currency{"DOT", ""} + DASH = Currency{"DASH", ""} + CRV = Currency{"CRV", ""} + ALGO = Currency{"ALGO", ""} //currency pair BTC_KRW = CurrencyPair{CurrencyA: BTC, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 1} @@ -68,33 +72,41 @@ var ( LTC_KRW = CurrencyPair{CurrencyA: LTC, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 2} BCH_KRW = CurrencyPair{CurrencyA: BCH, CurrencyB: KRW, AmountTickSize: 2, PriceTickSize: 2} - BTC_USD = CurrencyPair{CurrencyA: BTC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 1} - LTC_USD = CurrencyPair{CurrencyA: LTC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} - ETH_USD = CurrencyPair{CurrencyA: ETH, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} - ETC_USD = CurrencyPair{CurrencyA: ETC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} - BCH_USD = CurrencyPair{CurrencyA: BCH, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} - XRP_USD = CurrencyPair{CurrencyA: XRP, CurrencyB: USD, AmountTickSize: 3, PriceTickSize: 3} - BCD_USD = CurrencyPair{CurrencyA: BCD, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 3} - EOS_USD = CurrencyPair{CurrencyA: EOS, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} - BTG_USD = CurrencyPair{CurrencyA: BTG, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} - BSV_USD = CurrencyPair{CurrencyA: BSV, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + BTC_USD = CurrencyPair{CurrencyA: BTC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 1} + LTC_USD = CurrencyPair{CurrencyA: LTC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + ETH_USD = CurrencyPair{CurrencyA: ETH, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + ETC_USD = CurrencyPair{CurrencyA: ETC, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + BCH_USD = CurrencyPair{CurrencyA: BCH, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + XRP_USD = CurrencyPair{CurrencyA: XRP, CurrencyB: USD, AmountTickSize: 3, PriceTickSize: 3} + BCD_USD = CurrencyPair{CurrencyA: BCD, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 3} + EOS_USD = CurrencyPair{CurrencyA: EOS, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + BTG_USD = CurrencyPair{CurrencyA: BTG, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + BSV_USD = CurrencyPair{CurrencyA: BSV, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + DOT_USD = CurrencyPair{CurrencyA: DOT, CurrencyB: USD, AmountTickSize: 3, PriceTickSize: 2} + DASH_USD = CurrencyPair{CurrencyA: DASH, CurrencyB: USD, AmountTickSize: 2, PriceTickSize: 2} + CRV_USD = CurrencyPair{CurrencyA: CRV, CurrencyB: USD, AmountTickSize: 4, PriceTickSize: 3} + ALGO_USD = CurrencyPair{CurrencyA: ALGO, CurrencyB: USD, AmountTickSize: 4, PriceTickSize: 4} - BTC_USDT = CurrencyPair{CurrencyA: BTC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 1} - LTC_USDT = CurrencyPair{CurrencyA: LTC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - BCH_USDT = CurrencyPair{CurrencyA: BCH, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - ETC_USDT = CurrencyPair{CurrencyA: ETC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} - ETH_USDT = CurrencyPair{CurrencyA: ETH, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - BCD_USDT = CurrencyPair{CurrencyA: BCD, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - NEO_USDT = CurrencyPair{CurrencyA: NEO, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - EOS_USDT = CurrencyPair{CurrencyA: EOS, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - XRP_USDT = CurrencyPair{CurrencyA: XRP, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - HSR_USDT = CurrencyPair{CurrencyA: HSR, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - BSV_USDT = CurrencyPair{CurrencyA: BSV, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - OKB_USDT = CurrencyPair{CurrencyA: OKB, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - HT_USDT = CurrencyPair{CurrencyA: HT, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 4} - BNB_USDT = CurrencyPair{CurrencyA: BNB, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} - PAX_USDT = CurrencyPair{CurrencyA: PAX, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} - TRX_USDT = CurrencyPair{CurrencyA: TRX, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} + BTC_USDT = CurrencyPair{CurrencyA: BTC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 1} + LTC_USDT = CurrencyPair{CurrencyA: LTC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + BCH_USDT = CurrencyPair{CurrencyA: BCH, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + ETC_USDT = CurrencyPair{CurrencyA: ETC, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} + ETH_USDT = CurrencyPair{CurrencyA: ETH, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + BCD_USDT = CurrencyPair{CurrencyA: BCD, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + NEO_USDT = CurrencyPair{CurrencyA: NEO, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + EOS_USDT = CurrencyPair{CurrencyA: EOS, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + XRP_USDT = CurrencyPair{CurrencyA: XRP, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + HSR_USDT = CurrencyPair{CurrencyA: HSR, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + BSV_USDT = CurrencyPair{CurrencyA: BSV, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + OKB_USDT = CurrencyPair{CurrencyA: OKB, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + HT_USDT = CurrencyPair{CurrencyA: HT, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 4} + BNB_USDT = CurrencyPair{CurrencyA: BNB, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + PAX_USDT = CurrencyPair{CurrencyA: PAX, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} + TRX_USDT = CurrencyPair{CurrencyA: TRX, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 3} + DOT_USDT = CurrencyPair{CurrencyA: DOT, CurrencyB: USDT, AmountTickSize: 3, PriceTickSize: 2} + DASH_USDT = CurrencyPair{CurrencyA: DASH, CurrencyB: USDT, AmountTickSize: 2, PriceTickSize: 2} + CRV_USDT = CurrencyPair{CurrencyA: CRV, CurrencyB: USDT, AmountTickSize: 3, PriceTickSize: 3} + ALGO_USDT = CurrencyPair{CurrencyA: ALGO, CurrencyB: USDT, AmountTickSize: 3, PriceTickSize: 4} XRP_EUR = CurrencyPair{CurrencyA: XRP, CurrencyB: EUR, AmountTickSize: 2, PriceTickSize: 4} @@ -121,6 +133,7 @@ var ( HT_BTC = CurrencyPair{CurrencyA: HT, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 7} BNB_BTC = CurrencyPair{CurrencyA: BNB, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 6} TRX_BTC = CurrencyPair{CurrencyA: TRX, CurrencyB: BTC, AmountTickSize: 2, PriceTickSize: 7} + DOT_BTC = CurrencyPair{CurrencyA: DOT, CurrencyB: BTC, AmountTickSize: 3, PriceTickSize: 6} ETC_ETH = CurrencyPair{CurrencyA: ETC, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} EOS_ETH = CurrencyPair{CurrencyA: EOS, CurrencyB: ETH, AmountTickSize: 2, PriceTickSize: 4} @@ -196,6 +209,8 @@ func NewCurrency(symbol, desc string) Currency { return BNB case "trx", "TRX": return TRX + case "dot", "DOT": + return DOT default: return Currency{strings.ToUpper(symbol), desc} } From 2c83cc270ae54e7262393637e00a3e385ebe7dba Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 28 Dec 2020 11:25:22 +0800 Subject: [PATCH 24/34] [hbdm] swap ws market api --- huobi/Hbdm_Swap_Ws.go | 234 +++++++++++++++++++++++++++++++++++++ huobi/Hbdm_Swap_Ws_test.go | 30 +++++ 2 files changed, 264 insertions(+) create mode 100644 huobi/Hbdm_Swap_Ws.go create mode 100644 huobi/Hbdm_Swap_Ws_test.go diff --git a/huobi/Hbdm_Swap_Ws.go b/huobi/Hbdm_Swap_Ws.go new file mode 100644 index 00000000..92eac313 --- /dev/null +++ b/huobi/Hbdm_Swap_Ws.go @@ -0,0 +1,234 @@ +package huobi + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + . "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "strings" + "sync" + "time" +) + +type HbdmSwapWs struct { + *WsBuilder + sync.Once + wsConn *WsConn + + tickerCallback func(*FutureTicker) + depthCallback func(*Depth) + tradeCallback func(*Trade, string) +} + +func NewHbdmSwapWs() *HbdmSwapWs { + ws := &HbdmSwapWs{WsBuilder: NewWsBuilder()} + ws.WsBuilder = ws.WsBuilder. + WsUrl("wss://api.hbdm.com/swap-ws"). + //ProxyUrl("socks5://127.0.0.1:1080"). + AutoReconnect(). + DecompressFunc(GzipDecompress). + ProtoHandleFunc(ws.handle) + return ws +} + +func (ws *HbdmSwapWs) SetCallbacks(tickerCallback func(*FutureTicker), + depthCallback func(*Depth), + tradeCallback func(*Trade, string)) { + ws.tickerCallback = tickerCallback + ws.depthCallback = depthCallback + ws.tradeCallback = tradeCallback +} + +func (ws *HbdmSwapWs) TickerCallback(call func(ticker *FutureTicker)) { + ws.tickerCallback = call +} +func (ws *HbdmSwapWs) TradeCallback(call func(trade *Trade, contract string)) { + ws.tradeCallback = call +} + +func (ws *HbdmSwapWs) DepthCallback(call func(depth *Depth)) { + ws.depthCallback = call +} + +func (ws *HbdmSwapWs) SubscribeTicker(pair CurrencyPair, contract string) error { + if ws.tickerCallback == nil { + return errors.New("please set ticker callback func") + } + + if contract == SWAP_CONTRACT { + return ws.subscribe(map[string]interface{}{ + "id": "ticker_1", + "sub": fmt.Sprintf("market.%s.detail", pair.ToSymbol("-"))}) + } + + return errors.New("not implement") +} + +func (ws *HbdmSwapWs) SubscribeDepth(pair CurrencyPair, contract string) error { + if ws.depthCallback == nil { + return errors.New("please set depth callback func") + } + + if contract == SWAP_CONTRACT { + return ws.subscribe(map[string]interface{}{ + "id": "swap.depth", + "sub": fmt.Sprintf("market.%s.depth.step6", pair.ToSymbol("-"))}) + } + + return errors.New("not implement") +} + +func (ws *HbdmSwapWs) SubscribeTrade(pair CurrencyPair, contract string) error { + if ws.tradeCallback == nil { + return errors.New("please set trade callback func") + } + + if contract == SWAP_CONTRACT { + return ws.subscribe(map[string]interface{}{ + "id": "swap_trade_3", + "sub": fmt.Sprintf("market.%s.trade.detail", pair.ToSymbol("-"))}) + } + + return errors.New("not implement") +} + +func (ws *HbdmSwapWs) subscribe(sub map[string]interface{}) error { + // log.Println(sub) + ws.connectWs() + return ws.wsConn.Subscribe(sub) +} + +func (ws *HbdmSwapWs) connectWs() { + ws.Do(func() { + ws.wsConn = ws.WsBuilder.Build() + }) +} + +func (ws *HbdmSwapWs) handle(msg []byte) error { + logger.Debug("ws message data:", string(msg)) + //心跳 + if bytes.Contains(msg, []byte("ping")) { + pong := bytes.ReplaceAll(msg, []byte("ping"), []byte("pong")) + ws.wsConn.SendMessage(pong) + return nil + } + + var resp WsResponse + err := json.Unmarshal(msg, &resp) + if err != nil { + return err + } + + if resp.Ch == "" { + logger.Warnf("[%s] ch == \"\" , msg=%s", ws.wsConn.WsUrl, string(msg)) + return nil + } + + ts := time.Now() + if resp.Ts > 0 { + ts = time.Unix(0, resp.Ts*int64(time.Millisecond)) + } + + pair, contract, err := ws.parseCurrencyAndContract(resp.Ch) + if err != nil { + logger.Errorf("[%s] parse currency and contract err=%s", ws.wsConn.WsUrl, err) + return err + } + + if strings.Contains(resp.Ch, ".depth.") { + var depResp DepthResponse + err := json.Unmarshal(resp.Tick, &depResp) + if err != nil { + return err + } + + dep := ParseDepthFromResponse(depResp) + dep.ContractType = contract + dep.Pair = pair + dep.UTime = ts + + ws.depthCallback(&dep) + + return nil + } + + if strings.HasSuffix(resp.Ch, "trade.detail") { + var tradeResp TradeResponse + err := json.Unmarshal(resp.Tick, &tradeResp) + if err != nil { + return err + } + + trades := ws.parseTrade(tradeResp) + for _, v := range trades { + v.Pair = pair + ws.tradeCallback(&v, contract) + } + + return nil + } + + if strings.HasSuffix(resp.Ch, ".detail") { + var detail DetailResponse + err := json.Unmarshal(resp.Tick, &detail) + if err != nil { + return err + } + + ticker := ws.parseTicker(detail) + ticker.ContractType = contract + ticker.Pair = pair + ticker.Date = uint64(detail.Id) + + ws.tickerCallback(&ticker) + + return nil + } + + logger.Errorf("[%s] unknown message, msg=%s", ws.wsConn.WsUrl, string(msg)) + + return nil +} + +func (ws *HbdmSwapWs) parseTicker(r DetailResponse) FutureTicker { + return FutureTicker{Ticker: &Ticker{Last: r.Close, High: r.High, Low: r.Low, Vol: r.Amount}} +} + +func (ws *HbdmSwapWs) parseCurrencyAndContract(ch string) (CurrencyPair, string, error) { + el := strings.Split(ch, ".") + + if len(el) < 2 { + return UNKNOWN_PAIR, "", errors.New(ch) + } + + pair := NewCurrencyPair3(el[1], "-") + if pair.CurrencyB.Eq(USD) { + return pair, SWAP_CONTRACT, nil + } + + return pair, SWAP_USDT_CONTRACT, nil +} + +func (ws *HbdmSwapWs) parseTrade(r TradeResponse) []Trade { + var trades []Trade + for _, v := range r.Data { + trades = append(trades, Trade{ + Tid: v.Id, + Price: v.Price, + Amount: v.Amount, + Type: AdaptTradeSide(v.Direction), + Date: v.Ts}) + } + return trades +} + +func (ws *HbdmSwapWs) adaptTime(tm string) int64 { + format := "2006-01-02 15:04:05" + day := time.Now().Format("2006-01-02") + local, _ := time.LoadLocation("Asia/Chongqing") + t, _ := time.ParseInLocation(format, day+" "+tm, local) + return t.UnixNano() / 1e6 + +} diff --git a/huobi/Hbdm_Swap_Ws_test.go b/huobi/Hbdm_Swap_Ws_test.go new file mode 100644 index 00000000..cba7b6a0 --- /dev/null +++ b/huobi/Hbdm_Swap_Ws_test.go @@ -0,0 +1,30 @@ +package huobi + +import ( + "github.com/nntaoli-project/goex" + "github.com/nntaoli-project/goex/internal/logger" + "testing" + "time" +) + +func TestNewHbdmSwapWs(t *testing.T) { + logger.SetLevel(logger.DEBUG) + + ws := NewHbdmSwapWs() + + ws.DepthCallback(func(depth *goex.Depth) { + t.Log(depth) + }) + ws.TickerCallback(func(ticker *goex.FutureTicker) { + t.Log(ticker.Date, ticker.Last, ticker.Buy, ticker.Sell, ticker.High, ticker.Low, ticker.Vol) + }) + ws.TradeCallback(func(trade *goex.Trade, contract string) { + t.Log(trade, contract) + }) + + //t.Log(ws.SubscribeDepth(goex.BTC_USD, goex.SWAP_CONTRACT)) + //t.Log(ws.SubscribeTicker(goex.BTC_USD, goex.SWAP_CONTRACT)) + t.Log(ws.SubscribeTrade(goex.BTC_USD , goex.SWAP_CONTRACT)) + + time.Sleep(time.Minute) +} From 40a38c26ac4d60265d65b59c3cac5a8ebbf3222d Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 28 Dec 2020 11:28:08 +0800 Subject: [PATCH 25/34] [APIBuildere] add new the hbdm swap ws --- builder/APIBuilder.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builder/APIBuilder.go b/builder/APIBuilder.go index 70de6597..a76eb748 100644 --- a/builder/APIBuilder.go +++ b/builder/APIBuilder.go @@ -331,6 +331,8 @@ func (builder *APIBuilder) BuildFuturesWs(exName string) (FuturesWsApi, error) { })), nil case HBDM: return huobi.NewHbdmWs(), nil + case HBDM_SWAP: + return huobi.NewHbdmSwapWs(), nil case BINANCE, BINANCE_FUTURES, BINANCE_SWAP: return binance.NewFuturesWs(), nil case BITMEX: From 6ef47f5af647bc00a86c6503ba1bb4cfabb9fb90 Mon Sep 17 00:00:00 2001 From: Kevin Kim Date: Tue, 5 Jan 2021 15:08:47 +0900 Subject: [PATCH 26/34] [bithumb] get balance for all currencies --- bithumb/bithumb.go | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/bithumb/bithumb.go b/bithumb/bithumb.go index 72ac7cfe..149c3358 100644 --- a/bithumb/bithumb.go +++ b/bithumb/bithumb.go @@ -11,6 +11,7 @@ import ( "net/url" "sort" "strconv" + "strings" "time" ) @@ -212,36 +213,17 @@ func (bit *Bithumb) GetAccount() (*Account, error) { datamap := retmap["data"].(map[string]interface{}) acc := new(Account) acc.SubAccounts = make(map[Currency]SubAccount) - acc.SubAccounts[LTC] = SubAccount{ - Currency: LTC, - Amount: ToFloat64(datamap["available_ltc"]), - ForzenAmount: ToFloat64(datamap["in_use_ltc"]), - LoanAmount: 0} - acc.SubAccounts[BTC] = SubAccount{ - Currency: BTC, - Amount: ToFloat64(datamap["available_btc"]), - ForzenAmount: ToFloat64(datamap["in_use_etc"]), - LoanAmount: 0} - acc.SubAccounts[ETH] = SubAccount{ - Currency: ETH, - Amount: ToFloat64(datamap["available_eth"]), - ForzenAmount: ToFloat64(datamap["in_use_eth"]), - LoanAmount: 0} - acc.SubAccounts[ETC] = SubAccount{ - Currency: ETC, - Amount: ToFloat64(datamap["available_etc"]), - ForzenAmount: ToFloat64(datamap["in_use_etc"]), - LoanAmount: 0} - acc.SubAccounts[BCH] = SubAccount{ - Currency: BCH, - Amount: ToFloat64(datamap["available_bch"]), - ForzenAmount: ToFloat64(datamap["in_use_bch"]), - LoanAmount: 0} - acc.SubAccounts[KRW] = SubAccount{ - Currency: KRW, - Amount: ToFloat64(datamap["available_krw"]), - ForzenAmount: ToFloat64(datamap["in_use_krw"]), - LoanAmount: 0} + for key := range datamap { + if strings.HasPrefix(key, "available_") { + t := strings.Split(key, "_") + currency := NewCurrency(strings.ToUpper(t[len(t)-1]), "") + acc.SubAccounts[currency] = SubAccount{ + Currency: currency, + Amount: ToFloat64(datamap[key]), + ForzenAmount: ToFloat64(datamap[fmt.Sprintf("in_use_%s", strings.ToLower(currency.String()))]), + LoanAmount: 0} + } + } //log.Println(datamap) acc.Exchange = bit.GetExchangeName() return acc, nil From 90d4b9fbbaebd02c050a2e499eecfdecda0b3f90 Mon Sep 17 00:00:00 2001 From: Zachary Date: Fri, 15 Jan 2021 17:15:10 +0800 Subject: [PATCH 27/34] [kucoin] add GetAccount func implement --- kucoin/kucoin.go | 29 ++++++++++++++++++++++++++++- kucoin/kucoin_test.go | 4 ++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/kucoin/kucoin.go b/kucoin/kucoin.go index 3c4d1190..b5cf4019 100644 --- a/kucoin/kucoin.go +++ b/kucoin/kucoin.go @@ -345,7 +345,34 @@ func (kc *KuCoin) GetOrderHistorys(currency CurrencyPair, optional ...OptionalPa } func (kc *KuCoin) GetAccount() (*Account, error) { - var account Account + accs, err := kc.Accounts("", "") + if err != nil { + log.Error("KuCoin GetAccount error:", err) + return nil, err + } + + account := Account{} + account.Exchange = kc.GetExchangeName() + account.SubAccounts = make(map[Currency]SubAccount) + + for _, v := range accs { + currency := NewCurrency(v.Currency, "").AdaptBccToBch() + // KuCoin同一币种可能有多种账户类型 + if sub, exist := account.SubAccounts[currency]; !exist { + account.SubAccounts[currency] = SubAccount{ + Currency: currency, + Amount: ToFloat64(v.Available), + ForzenAmount: ToFloat64(v.Holds), + } + } else { + account.SubAccounts[currency] = SubAccount{ + Currency: currency, + Amount: sub.Amount + ToFloat64(v.Available), + ForzenAmount: sub.ForzenAmount + ToFloat64(v.Holds), + } + } + + } return &account, nil } diff --git a/kucoin/kucoin_test.go b/kucoin/kucoin_test.go index 8e64489d..ea419fdb 100644 --- a/kucoin/kucoin_test.go +++ b/kucoin/kucoin_test.go @@ -27,3 +27,7 @@ func TestKuCoin_GetTrades(t *testing.T) { t.Log(trades) } +func TestKuCoin_GetAccount(t *testing.T) { + acc, _ := kc.GetAccount() + t.Log(acc) +} From 6fb8b7abfcd6aeaf7f25329176db9930cff13620 Mon Sep 17 00:00:00 2001 From: Zachary Date: Fri, 15 Jan 2021 17:19:05 +0800 Subject: [PATCH 28/34] [kucoin] fix kucoin api url --- kucoin/kucoin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kucoin/kucoin.go b/kucoin/kucoin.go index b5cf4019..d6833d78 100644 --- a/kucoin/kucoin.go +++ b/kucoin/kucoin.go @@ -9,7 +9,7 @@ import ( func New(apiKey, apiSecret, apiPassphrase string) *KuCoin { return NewWithConfig(&APIConfig{ - Endpoint: "https://api.kcs.top", + Endpoint: "https://api.kucoin.com", ApiKey: apiKey, ApiSecretKey: apiSecret, ApiPassphrase: apiPassphrase, From 6351616297c90c798f17ada8eb56d07000bd1b04 Mon Sep 17 00:00:00 2001 From: kenshou Date: Sun, 24 Jan 2021 00:32:23 +0800 Subject: [PATCH 29/34] =?UTF-8?q?feat:=20=E5=8A=A0=E5=85=A5usdt=E6=B0=B8?= =?UTF-8?q?=E7=BB=AD=E5=90=88=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- huobi/Hbdm_Swap_Ws.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/huobi/Hbdm_Swap_Ws.go b/huobi/Hbdm_Swap_Ws.go index 92eac313..0e1e60d9 100644 --- a/huobi/Hbdm_Swap_Ws.go +++ b/huobi/Hbdm_Swap_Ws.go @@ -33,6 +33,18 @@ func NewHbdmSwapWs() *HbdmSwapWs { return ws } +//构建usdt本位永续合约ws +func NewHbdmLinearSwapWs() *HbdmSwapWs { + ws := &HbdmSwapWs{WsBuilder: NewWsBuilder()} + ws.WsBuilder = ws.WsBuilder. + WsUrl("wss://api.hbdm.com/linear-swap-ws"). + //ProxyUrl("socks5://127.0.0.1:1080"). + AutoReconnect(). + DecompressFunc(GzipDecompress). + ProtoHandleFunc(ws.handle) + return ws +} + func (ws *HbdmSwapWs) SetCallbacks(tickerCallback func(*FutureTicker), depthCallback func(*Depth), tradeCallback func(*Trade, string)) { @@ -57,7 +69,7 @@ func (ws *HbdmSwapWs) SubscribeTicker(pair CurrencyPair, contract string) error return errors.New("please set ticker callback func") } - if contract == SWAP_CONTRACT { + if contract == SWAP_CONTRACT || contract == SWAP_USDT_CONTRACT { return ws.subscribe(map[string]interface{}{ "id": "ticker_1", "sub": fmt.Sprintf("market.%s.detail", pair.ToSymbol("-"))}) @@ -71,7 +83,7 @@ func (ws *HbdmSwapWs) SubscribeDepth(pair CurrencyPair, contract string) error { return errors.New("please set depth callback func") } - if contract == SWAP_CONTRACT { + if contract == SWAP_CONTRACT || contract == SWAP_USDT_CONTRACT { return ws.subscribe(map[string]interface{}{ "id": "swap.depth", "sub": fmt.Sprintf("market.%s.depth.step6", pair.ToSymbol("-"))}) @@ -85,7 +97,7 @@ func (ws *HbdmSwapWs) SubscribeTrade(pair CurrencyPair, contract string) error { return errors.New("please set trade callback func") } - if contract == SWAP_CONTRACT { + if contract == SWAP_CONTRACT || contract == SWAP_USDT_CONTRACT { return ws.subscribe(map[string]interface{}{ "id": "swap_trade_3", "sub": fmt.Sprintf("market.%s.trade.detail", pair.ToSymbol("-"))}) From d8ffa380d604e58be012a21323e930059d7523a6 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Sat, 30 Jan 2021 15:21:43 +0800 Subject: [PATCH 30/34] [binance] Fill OrderID2 --- binance/Binance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binance/Binance.go b/binance/Binance.go index fb1b6fb1..01bbcdf8 100644 --- a/binance/Binance.go +++ b/binance/Binance.go @@ -653,7 +653,7 @@ func (bn *Binance) adaptOrder(currencyPair CurrencyPair, orderMap map[string]int return Order{ OrderID: ToInt(orderMap["orderId"]), - OrderID2: fmt.Sprint(orderMap["orderId"]), + OrderID2: fmt.Sprintf("%.0f",orderMap["orderId"]), Cid: orderMap["clientOrderId"].(string), Currency: currencyPair, Price: ToFloat64(orderMap["price"]), From a5380d3daa593d3cf8719186c862ddc70fc675b3 Mon Sep 17 00:00:00 2001 From: Jonarod Date: Sun, 14 Feb 2021 00:03:37 +0100 Subject: [PATCH 31/34] add bitstamp Klines --- HttpUtils.go | 7 +++-- bitstamp/Bitstamp.go | 69 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/HttpUtils.go b/HttpUtils.go index 5dd1e89f..49967bb0 100644 --- a/HttpUtils.go +++ b/HttpUtils.go @@ -5,9 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/nntaoli-project/goex/internal/logger" - "github.com/valyala/fasthttp" - "github.com/valyala/fasthttp/fasthttpproxy" "io/ioutil" "log" "net/http" @@ -15,6 +12,10 @@ import ( "os" "strings" "time" + + "github.com/nntaoli-project/goex/internal/logger" + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttpproxy" ) var ( diff --git a/bitstamp/Bitstamp.go b/bitstamp/Bitstamp.go index 5a340f2d..2d994e30 100644 --- a/bitstamp/Bitstamp.go +++ b/bitstamp/Bitstamp.go @@ -4,19 +4,39 @@ import ( "encoding/json" "errors" "fmt" - . "github.com/nntaoli-project/goex" "net/http" "net/url" "sort" "strconv" "strings" "time" + + . "github.com/nntaoli-project/goex" ) var ( BASE_URL = "https://www.bitstamp.net/api/" ) +var _INTERNAL_KLINE_PERIOD_CONVERTER = map[KlinePeriod]string{ + KLINE_PERIOD_1MIN: "60", + KLINE_PERIOD_3MIN: "180", + KLINE_PERIOD_5MIN: "300", + KLINE_PERIOD_15MIN: "900", + KLINE_PERIOD_30MIN: "1800", + KLINE_PERIOD_60MIN: "3600", + KLINE_PERIOD_1H: "3600", + KLINE_PERIOD_2H: "7200", + KLINE_PERIOD_4H: "14400", + KLINE_PERIOD_6H: "21600", + // KLINE_PERIOD_8H: "28800", // Not supported + KLINE_PERIOD_12H: "43200", + KLINE_PERIOD_1DAY: "86400", + KLINE_PERIOD_3DAY: "259200", + // KLINE_PERIOD_1WEEK: "604800", //Not supported + // KLINE_PERIOD_1MONTH: "1M", // Not supported +} + type Bitstamp struct { client *http.Client clientId, @@ -389,7 +409,52 @@ func (bitstamp *Bitstamp) GetDepth(size int, currency CurrencyPair) (*Depth, err } func (bitstamp *Bitstamp) GetKlineRecords(currency CurrencyPair, period KlinePeriod, size int, optional ...OptionalParameter) ([]Kline, error) { - panic("not implement") + + params := url.Values{} + params.Set("step", _INTERNAL_KLINE_PERIOD_CONVERTER[period]) + params.Set("limit", fmt.Sprintf("%d", size)) + MergeOptionalParameter(¶ms, optional...) + + urlStr := BASE_URL + "v2/ohlc/" + strings.ToLower(currency.ToSymbol("")) + "?" + params.Encode() + + fmt.Println(urlStr) + + type ohlcResp struct { + Data struct { + Pair string `json:"pair"` + Ohlc []struct { + High string `json:"high"` + Timestamp string `json:"timestamp"` + Volume string `json:"volume"` + Low string `json:"low"` + Close string `json:"close"` + Open string `json:"open"` + } `json:ohlc` + } `json:"data"` + } + + resp := ohlcResp{} + err := HttpGet4(bitstamp.client, urlStr, nil, &resp) + if err != nil { + return nil, err + } + + var klineRecords []Kline + + for _, _record := range resp.Data.Ohlc { + r := Kline{Pair: currency} + r.Timestamp, _ = strconv.ParseInt(_record.Timestamp, 10, 64) //to unix timestramp + r.Open = ToFloat64(_record.Open) + r.High = ToFloat64(_record.High) + r.Low = ToFloat64(_record.Low) + r.Close = ToFloat64(_record.Close) + r.Vol = ToFloat64(_record.Volume) + + klineRecords = append(klineRecords, r) + } + + return klineRecords, nil + } ////非个人,整个交易所的交易记录 From 6305d5e6fa1fb690b7663774c7913c407a4df3d3 Mon Sep 17 00:00:00 2001 From: qct Date: Tue, 16 Feb 2021 23:58:27 +0800 Subject: [PATCH 32/34] Add GetKlineRecordsByRange for okex swap --- okex/OKExSwap.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/okex/OKExSwap.go b/okex/OKExSwap.go index 3605380a..daebe7ef 100644 --- a/okex/OKExSwap.go +++ b/okex/OKExSwap.go @@ -571,6 +571,44 @@ func (ok *OKExSwap) GetKlineRecords(contractType string, currency CurrencyPair, return ok.GetKlineRecords2(contractType, currency, "", "", strconv.Itoa(granularity)) } +/** + since : 单位秒,开始时间 + to : 单位秒,结束时间 +*/ +func (ok *OKExSwap) GetKlineRecordsByRange(currency CurrencyPair, period, since, to int) ([]FutureKline, error) { + urlPath := "/api/swap/v3/instruments/%s/candles?start=%s&end=%s&granularity=%d" + sinceTime := time.Unix(int64(since), 0).UTC().Format(time.RFC3339) + toTime := time.Unix(int64(to), 0).UTC().Format(time.RFC3339) + contractId := ok.adaptContractType(currency) + granularity := adaptKLinePeriod(KlinePeriod(period)) + if granularity == -1 { + return nil, errors.New("kline period parameter is error") + } + + var response [][]interface{} + err := ok.DoRequest("GET", fmt.Sprintf(urlPath, contractId, sinceTime, toTime, granularity), "", &response) + if err != nil { + return nil, err + } + + var klines []FutureKline + for _, itm := range response { + t, _ := time.Parse(time.RFC3339, fmt.Sprint(itm[0])) + klines = append(klines, FutureKline{ + Kline: &Kline{ + Timestamp: t.Unix(), + Pair: currency, + Open: ToFloat64(itm[1]), + High: ToFloat64(itm[2]), + Low: ToFloat64(itm[3]), + Close: ToFloat64(itm[4]), + Vol: ToFloat64(itm[5])}, + Vol2: ToFloat64(itm[6])}) + } + + return klines, nil +} + /** since : 单位秒,开始时间 */ From 53ab3a9f900bac2d73a22863f0b028d01570f2e1 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Mon, 22 Feb 2021 00:45:58 +0800 Subject: [PATCH 33/34] [hbdm] implement the swap get history orders api Signed-off-by: nntaoli <2767415655@qq.com> --- huobi/Hbdm.go | 1 + huobi/Hbdm_Swap.go | 47 ++++++++++++++++++++++++++++++++++++++++- huobi/Hbdm_Swap_test.go | 7 ++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/huobi/Hbdm.go b/huobi/Hbdm.go index fc13a5ba..2539f1a0 100644 --- a/huobi/Hbdm.go +++ b/huobi/Hbdm.go @@ -32,6 +32,7 @@ type OrderInfo struct { ClientOrderId int64 `json:"client_order_id"` OrderSource string `json:"order_source"` CreatedAt int64 `json:"created_at"` + CreateDate int64 `json:"create_date"` //for swap contract TradeVolume float64 `json:"trade_volume"` TradeTurnover float64 `json:"trade_turnover"` Fee float64 `json:"fee"` diff --git a/huobi/Hbdm_Swap.go b/huobi/Hbdm_Swap.go index 428f2394..6dcbe19f 100644 --- a/huobi/Hbdm_Swap.go +++ b/huobi/Hbdm_Swap.go @@ -27,6 +27,7 @@ const ( cancelOrderApiPath = "/swap-api/v1/swap_cancel" getOpenOrdersApiPath = "/swap-api/v1/swap_openorders" getOrderInfoApiPath = "/swap-api/v1/swap_order_info" + getHistoryOrderPath = "/swap-api/v1/swap_hisorders_exact" ) func NewHbdmSwap(c *APIConfig) *HbdmSwap { @@ -382,7 +383,51 @@ func (swap *HbdmSwap) GetFutureOrder(orderId string, currencyPair CurrencyPair, } func (swap *HbdmSwap) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { - panic("implement me") + params := url.Values{} + params.Add("status", "0") //all + params.Add("type", "1") //all + params.Add("trade_type", "0") //all + + if contractType == "" || contractType == SWAP_CONTRACT { + params.Add("contract_code", pair.AdaptUsdtToUsd().ToSymbol("-")) + } else { + return nil, errors.New("contract type is error") + } + + MergeOptionalParameter(¶ms, optional...) + + var historyOrderResp struct { + Orders []OrderInfo `json:"orders"` + RemainSize int64 `json:"remain_size"` + NextId int64 `json:"next_id"` + } + + err := swap.base.doRequest(getHistoryOrderPath, ¶ms, &historyOrderResp) + if err != nil { + return nil, err + } + + var historyOrders []FutureOrder + + for _, ord := range historyOrderResp.Orders { + historyOrders = append(historyOrders, FutureOrder{ + OrderID: ord.OrderId, + OrderID2: fmt.Sprintf("%d", ord.OrderId), + Price: ord.Price, + Amount: ord.Volume, + AvgPrice: ord.TradeAvgPrice, + DealAmount: ord.TradeVolume, + OrderTime: ord.CreateDate, + Status: swap.base.adaptOrderStatus(ord.Status), + Currency: pair, + OType: swap.base.adaptOffsetDirectionToOpenType(ord.Offset, ord.Direction), + LeverRate: ord.LeverRate, + Fee: ord.Fee, + ContractName: ord.ContractCode, + }) + } + + return historyOrders, nil } func (swap *HbdmSwap) GetUnfinishFutureOrders(currencyPair CurrencyPair, contractType string) ([]FutureOrder, error) { diff --git a/huobi/Hbdm_Swap_test.go b/huobi/Hbdm_Swap_test.go index 27b899fe..f5619875 100644 --- a/huobi/Hbdm_Swap_test.go +++ b/huobi/Hbdm_Swap_test.go @@ -4,6 +4,7 @@ import ( "github.com/nntaoli-project/goex" "net/http" "testing" + "time" ) var swap *HbdmSwap @@ -53,3 +54,9 @@ func TestHbdmSwap_GetUnfinishFutureOrders(t *testing.T) { func TestHbdmSwap_GetFutureOrder(t *testing.T) { t.Log(swap.GetFutureOrder("784118017750929408", goex.NewCurrencyPair2("DOT_USD"), goex.SWAP_CONTRACT)) } + +func TestHbdmSwap_GetFutureOrderHistory(t *testing.T) { + t.Log(swap.GetFutureOrderHistory(goex.NewCurrencyPair2("KSM_USD"), goex.SWAP_CONTRACT, + goex.OptionalParameter{}.Optional("start_time", time.Now().Add(-5*24*time.Hour).Unix()*1000), + goex.OptionalParameter{}.Optional("end_time", time.Now().Unix()*1000))) +} From da4136cf6190c53b7b58994e0a7feed0af8a4056 Mon Sep 17 00:00:00 2001 From: nntaoli <2767415655@qq.com> Date: Thu, 25 Feb 2021 22:31:42 +0800 Subject: [PATCH 34/34] [hbdm] get order historys api --- huobi/Hbdm.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/huobi/Hbdm.go b/huobi/Hbdm.go index 2539f1a0..72a025a1 100644 --- a/huobi/Hbdm.go +++ b/huobi/Hbdm.go @@ -459,7 +459,48 @@ func (dm *Hbdm) GetFutureOrders(orderIds []string, currencyPair CurrencyPair, co } func (dm *Hbdm) GetFutureOrderHistory(pair CurrencyPair, contractType string, optional ...OptionalParameter) ([]FutureOrder, error) { - panic("implement me") + path := "/api/v1/contract_hisorders_exact" + + param := url.Values{} + param.Set("symbol", pair.CurrencyA.Symbol) + param.Set("type", "1") + param.Set("trade_type", "0") + param.Set("status", "0") + param.Set("size", "50") + + MergeOptionalParameter(¶m, optional...) + + var data struct { + Orders []OrderInfo `json:"orders"` + RemainSize int `json:"remain_size"` + NextId int `json:"next_id"` + } + + err := dm.doRequest(path, ¶m, &data) + if err != nil { + return nil, err + } + + var ords []FutureOrder + for _, ord := range data.Orders { + ords = append(ords, FutureOrder{ + ContractName: ord.ContractType, + Currency: pair, + OType: dm.adaptOffsetDirectionToOpenType(ord.Offset, ord.Direction), + OrderID2: fmt.Sprint(ord.OrderId), + OrderID: ord.OrderId, + Amount: ord.Volume, + Price: ord.Price, + AvgPrice: ord.TradeAvgPrice, + DealAmount: ord.TradeVolume, + Status: dm.adaptOrderStatus(ord.Status), + Fee: ord.Fee, + LeverRate: ord.LeverRate, + OrderTime: ord.CreateDate, + }) + } + + return ords, nil } func (dm *Hbdm) GetContractValue(currencyPair CurrencyPair) (float64, error) {