From 295072a62375206832baef79b51207b3a9b445a7 Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 15:04:31 +0200 Subject: [PATCH 1/9] no need to install deps again --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 89dfd987..268c0b08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,8 +5,8 @@ COPY Gopkg.* ./ RUN dep ensure -vendor-only COPY . . RUN mv -vn config_example.json config.json \ - && GOARCH=386 GOOS=linux CGO_ENABLED=0 go install -v \ - && mv /go/bin/linux_386 /go/bin/gocryptotrader + && GOARCH=386 GOOS=linux CGO_ENABLED=0 go build . \ + && mv gocryptotrader /go/bin/gocryptotrader FROM alpine:latest RUN apk update && apk add --no-cache ca-certificates From f9849eae31fd15a52ecee8b560580cbc2da45f33 Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 15:14:08 +0200 Subject: [PATCH 2/9] added gorilla websocket conn and mutex --- exchanges/okex/okex.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exchanges/okex/okex.go b/exchanges/okex/okex.go index f7cd944f..56b927ce 100644 --- a/exchanges/okex/okex.go +++ b/exchanges/okex/okex.go @@ -8,8 +8,10 @@ import ( "reflect" "strconv" "strings" + "sync" "time" + "github.com/gorilla/websocket" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" exchange "github.com/thrasher-/gocryptotrader/exchanges" @@ -78,6 +80,8 @@ var errMissValue = errors.New("warning - resp value is missing from exchange") // OKEX is the overaching type across the OKEX methods type OKEX struct { exchange.Base + WebsocketConn *websocket.Conn + mu sync.Mutex // Spot and contract market error codes as per https://www.okex.com/rest_request.html ErrorCodes map[string]error From ff5c1eb62b9f57f6b4600eb3ef5afc5261c69d2a Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 15:14:24 +0200 Subject: [PATCH 3/9] added okex websocket types --- exchanges/okex/okex_types.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/exchanges/okex/okex_types.go b/exchanges/okex/okex_types.go index 180eb7f0..403962d4 100644 --- a/exchanges/okex/okex_types.go +++ b/exchanges/okex/okex_types.go @@ -1,5 +1,7 @@ package okex +import "encoding/json" + // ContractPrice holds date and ticker price price for contracts. type ContractPrice struct { Date string `json:"date"` @@ -17,6 +19,33 @@ type ContractPrice struct { Error interface{} `json:"error_code"` } +type MultiStreamData struct { + Channel string `json:"channel"` + Data json.RawMessage `json:"data"` +} + +type TickerStreamData struct { + Buy string `json:"buy"` + Change string `json:"change"` + High string `json:"high"` + Low string `json:"low"` + Last string `json:"last"` + Sell string `json:"sell"` + DayLow string `json:"dayLow"` + DayHigh string `json:"dayHigh"` + Timestamp float64 `json:"timestamp"` + Vol string `json:"vol"` +} + +type DealsStreamData = [][]string +type KlineStreamData = [][]string + +type DepthStreamData struct { + Asks [][]string `json:"asks"` + Bids [][]string `json:"bids"` + Timestamp float64 `json:"timestamp"` +} + // ContractDepth response depth type ContractDepth struct { Asks []interface{} `json:"asks"` From 0f26026a15d6578373b68064f37144c73cd0a1a3 Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 15:15:21 +0200 Subject: [PATCH 4/9] added websocket logic --- exchanges/okex/okex_websocket.go | 150 +++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 exchanges/okex/okex_websocket.go diff --git a/exchanges/okex/okex_websocket.go b/exchanges/okex/okex_websocket.go new file mode 100644 index 00000000..90cea0d6 --- /dev/null +++ b/exchanges/okex/okex_websocket.go @@ -0,0 +1,150 @@ +package okex + +import ( + "fmt" + "log" + "net/http" + "strings" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-/gocryptotrader/common" +) + +const ( + okexDefaultWebsocketURL = "wss://real.okex.com:10440/websocket/okexapi" +) + +func (o *OKEX) writeToWebsocket(message string) error { + o.mu.Lock() + defer o.mu.Unlock() + + return o.WebsocketConn.WriteMessage(websocket.TextMessage, []byte(message)) +} + +func (o *OKEX) websocketConnect() { + var Dialer websocket.Dialer + var err error + myEnabledSubscriptionChannels := []string{} + + for _, pair := range o.EnabledPairs { + myEnabledSubscriptionChannels = append(myEnabledSubscriptionChannels, fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_ticker'}", pair)) + myEnabledSubscriptionChannels = append(myEnabledSubscriptionChannels, fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_depth'}", pair)) + myEnabledSubscriptionChannels = append(myEnabledSubscriptionChannels, fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_deals'}", pair)) + myEnabledSubscriptionChannels = append(myEnabledSubscriptionChannels, fmt.Sprintf("{'event':'addChannel','channel':'ok_sub_spot_%s_kline_1min'}", pair)) + } + + mySubscriptionString := "[" + strings.Join(myEnabledSubscriptionChannels, ",") + "]" + + o.WebsocketConn, _, err = Dialer.Dial(okexDefaultWebsocketURL, http.Header{}) + + if err != nil { + log.Printf("%s Unable to connect to Websocket. Error: %s\n", o.Name, err) + return + } + + if o.Verbose { + log.Printf("%s Connected to Websocket.\n", o.Name) + log.Printf("Subscription String is %s\n", mySubscriptionString) + } + + log.Printf("Subscription String is %s\n", mySubscriptionString) + + // subscribe to all the desired subscriptions + err = o.writeToWebsocket(mySubscriptionString) + + if err != nil { + log.Printf("Error: Could not subscribe to the OKEX websocket %s", err) + return + } +} + +// WebsocketClient the main function handling the OKEX websocket +// Documentation URL: https://github.com/okcoin-okex/API-docs-OKEx.com/blob/master/API-For-Spot-EN/WEBSOCKET%20API%20for%20SPOT.md +func (o *OKEX) WebsocketClient() { + for o.Enabled && o.Websocket { + o.websocketConnect() + + go func() { + for { + time.Sleep(time.Second * 27) + o.writeToWebsocket("{'event':'ping'}") + log.Printf("%s sent Ping message\n", o.GetName()) + } + }() + + for o.Enabled && o.Websocket { + msgType, resp, err := o.WebsocketConn.ReadMessage() + + if err != nil { + log.Printf("Error: Could not read from the OKEX websocket %s", err) + o.websocketConnect() + continue + } + + switch msgType { + case websocket.TextMessage: + multiStreamDataArr := []MultiStreamData{} + + err = common.JSONDecode(resp, &multiStreamDataArr) + + if err != nil { + if strings.Contains(string(resp), "pong") { + log.Printf("%s received Pong message\n", o.GetName()) + } else { + log.Printf("%s some other error happened: %s", o.GetName(), err) + continue + } + } + + for _, multiStreamData := range multiStreamDataArr { + if strings.Contains(multiStreamData.Channel, "ticker") { + // ticker data + ticker := TickerStreamData{} + tickerDecodeError := common.JSONDecode(multiStreamData.Data, &ticker) + + if tickerDecodeError != nil { + log.Printf("OKEX Ticker Decode Error: %s", tickerDecodeError) + continue + } + + log.Printf("OKEX Channel: %s\tData: %s\n", multiStreamData.Channel, multiStreamData.Data) + } else if strings.Contains(multiStreamData.Channel, "deals") { + // orderbook data + deals := DealsStreamData{} + decodeError := common.JSONDecode(multiStreamData.Data, &deals) + + if decodeError != nil { + log.Printf("OKEX Deals Decode Error: %s", decodeError) + continue + } + + log.Printf("OKEX Channel: %s\tData: %s\n", multiStreamData.Channel, multiStreamData.Data) + } else if strings.Contains(multiStreamData.Channel, "kline") { + // 1 min kline data + klines := KlineStreamData{} + decodeError := common.JSONDecode(multiStreamData.Data, &klines) + + if decodeError != nil { + log.Printf("OKEX Klines Decode Error: %s", decodeError) + continue + } + + log.Printf("OKEX Channel: %s\tData: %s\n", multiStreamData.Channel, multiStreamData.Data) + } else if strings.Contains(multiStreamData.Channel, "depth") { + // market depth data + depth := DepthStreamData{} + decodeError := common.JSONDecode(multiStreamData.Data, &depth) + + if decodeError != nil { + log.Printf("OKEX Depth Decode Error: %s", decodeError) + continue + } + + log.Printf("OKEX Channel: %s\tData: %s\n", multiStreamData.Channel, multiStreamData.Data) + } + } + } + } + } +} From 6fc05d46d263188ac26a9731829f2e4722f05dfd Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 15:15:34 +0200 Subject: [PATCH 5/9] running the websocket if enabled --- exchanges/okex/okex_wrapper.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index 6c8f3afa..15942258 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -2,12 +2,12 @@ package okex import ( "errors" + exchange "gocryptotrader/exchanges" "log" "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" - "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/orderbook" "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) @@ -28,6 +28,10 @@ func (o *OKEX) Run() { log.Printf("%s polling delay: %ds.\n", o.GetName(), o.RESTPollingDelay) log.Printf("%s %d currencies enabled: %s.\n", o.GetName(), len(o.EnabledPairs), o.EnabledPairs) } + + if o.Websocket { + go o.WebsocketClient() + } } // UpdateTicker updates and returns the ticker for a currency pair From f4602fb1f93e7be7ee998ec514075fa712a59b9d Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 15:21:00 +0200 Subject: [PATCH 6/9] fix typo --- exchanges/okex/okex_wrapper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index 15942258..39073f70 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -2,12 +2,12 @@ package okex import ( "errors" - exchange "gocryptotrader/exchanges" "log" "sync" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency/pair" + exchange "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/orderbook" "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) From a7baed25d67ad53de501c977ef26cb2350189575 Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 17:46:44 +0200 Subject: [PATCH 7/9] okex pairs fixes --- testdata/configtest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testdata/configtest.json b/testdata/configtest.json index 13342a77..7dd6c37c 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -1033,8 +1033,8 @@ "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "ltc_btc,eth_btc,etc_btc,bch_btc,btc_usdt,eth_usdt,ltc_usdt,etc_usdt,bch_usdt,etc_eth,bt1_btc,bt2_btc,btg_btc,qtum_btc,hsr_btc,neo_btc,gas_btc,qtum_usdt,hsr_usdt,neo_usdt,gas_usdt,btc_usd,ltc_usd,eth_usd,etc_usd,bch_usd", - "EnabledPairs": "btc_usd,ltc_usd", + "AvailablePairs": "ltc_btc,eth_btc,etc_btc,bch_btc,btc_usdt,eth_usdt,ltc_usdt,etc_usdt,bch_usdt,etc_eth,bt1_btc,bt2_btc,btg_btc,qtum_btc,hsr_btc,neo_btc,gas_btc,qtum_usdt,hsr_usdt,neo_usdt,gas_usdt,btc_usdt,ltc_usdt,eth_usdt,etc_usdt,bch_usdt", + "EnabledPairs": "btc_usdt,ltc_usdt", "BaseCurrencies": "USD", "AssetTypes": "SPOT,this_week,next_week,quarter", "SupportsAutoPairUpdates": false, From 7377dca9a9bb8b4484338ba5dbde55207980519e Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 17:51:02 +0200 Subject: [PATCH 8/9] okex config pairs fixes --- config_example.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config_example.json b/config_example.json index f887ba2c..25e0df11 100644 --- a/config_example.json +++ b/config_example.json @@ -987,8 +987,8 @@ "AuthenticatedAPISupport": false, "APIKey": "Key", "APISecret": "Secret", - "AvailablePairs": "ltc_btc,eth_btc,etc_btc,bch_btc,btc_usdt,eth_usdt,ltc_usdt,etc_usdt,bch_usdt,etc_eth,bt1_btc,bt2_btc,btg_btc,qtum_btc,hsr_btc,neo_btc,gas_btc,qtum_usdt,hsr_usdt,neo_usdt,gas_usdt,btc_usd,ltc_usd,eth_usd,etc_usd,bch_usd", - "EnabledPairs": "btc_usd,ltc_usd", + "AvailablePairs": "ltc_btc,eth_btc,etc_btc,bch_btc,btc_usdt,eth_usdt,ltc_usdt,etc_usdt,bch_usdt,etc_eth,bt1_btc,bt2_btc,btg_btc,qtum_btc,hsr_btc,neo_btc,gas_btc,qtum_usdt,hsr_usdt,neo_usdt,gas_usdt,btc_usdt,ltc_usdt,eth_usdt,etc_usdt,bch_usdt", + "EnabledPairs": "btc_usdt,ltc_usdt", "BaseCurrencies": "USD", "AssetTypes": "SPOT,this_week,next_week,quarter", "SupportsAutoPairUpdates": false, From cc7caf1a32ca5fd9e4044acc02008935fd26b78d Mon Sep 17 00:00:00 2001 From: Ermal Guni Date: Sun, 5 Aug 2018 18:31:01 +0200 Subject: [PATCH 9/9] contract_id json error conversion --- exchanges/okex/okex_types.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exchanges/okex/okex_types.go b/exchanges/okex/okex_types.go index 403962d4..dbcb792b 100644 --- a/exchanges/okex/okex_types.go +++ b/exchanges/okex/okex_types.go @@ -7,7 +7,7 @@ type ContractPrice struct { Date string `json:"date"` Ticker struct { Buy float64 `json:"buy"` - ContractID int `json:"contract_id"` + ContractID float64 `json:"contract_id"` High float64 `json:"high"` Low float64 `json:"low"` Last float64 `json:"last"` @@ -112,7 +112,7 @@ type HoldData struct { BuyPriceAvg float64 `json:"buy_price_avg"` BuyPriceCost float64 `json:"buy_price_cost"` BuyProfitReal float64 `json:"buy_profit_real"` - ContractID int `json:"contract_id"` + ContractID float64 `json:"contract_id"` ContractType string `json:"contract_type"` CreateDate int `json:"create_date"` LeverRate float64 `json:"lever_rate"` @@ -144,7 +144,7 @@ type SpotPrice struct { Date string `json:"date"` Ticker struct { Buy float64 `json:"buy,string"` - ContractID int `json:"contract_id"` + ContractID float64 `json:"contract_id"` High float64 `json:"high,string"` Low float64 `json:"low,string"` Last float64 `json:"last,string"`