mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
Merge pull request #169 from ermalguni/master
OKEX websocket resolves #158
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1036,32 +1036,31 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OKEX",
|
||||
"enabled": true,
|
||||
"verbose": false,
|
||||
"websocket": false,
|
||||
"useSandbox": false,
|
||||
"restPollingDelay": 10,
|
||||
"httpTimeout": 15000000000,
|
||||
"httpUserAgent": "",
|
||||
"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",
|
||||
"baseCurrencies": "USD",
|
||||
"assetTypes": "SPOT,this_week,next_week,quarter",
|
||||
"supportsAutoPairUpdates": false,
|
||||
"pairsLastUpdated": 1522111402,
|
||||
"configCurrencyPairFormat": {
|
||||
"uppercase": false,
|
||||
"delimiter": "_"
|
||||
"Name": "OKEX",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"UseSandbox": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"HTTPTimeout": 15000000000,
|
||||
"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_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,
|
||||
"PairsLastUpdated": 1522111402,
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"requestCurrencyPairFormat": {
|
||||
"uppercase": false,
|
||||
"delimiter": "_"
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"bankAccounts": [
|
||||
"BankAccounts": [
|
||||
{
|
||||
"bankName": "",
|
||||
"bankAddress": "",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package okex
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// ContractPrice holds date and ticker price price for contracts.
|
||||
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"`
|
||||
@@ -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"`
|
||||
@@ -83,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"`
|
||||
@@ -115,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"`
|
||||
|
||||
150
exchanges/okex/okex_websocket.go
Normal file
150
exchanges/okex/okex_websocket.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
exchange "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
|
||||
|
||||
47
testdata/configtest.json
vendored
47
testdata/configtest.json
vendored
@@ -1048,32 +1048,31 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OKEX",
|
||||
"enabled": true,
|
||||
"verbose": false,
|
||||
"websocket": false,
|
||||
"useSandbox": false,
|
||||
"restPollingDelay": 10,
|
||||
"httpTimeout": 15000000000,
|
||||
"httpUserAgent": "",
|
||||
"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",
|
||||
"baseCurrencies": "USD",
|
||||
"assetTypes": "SPOT,this_week,next_week,quarter",
|
||||
"supportsAutoPairUpdates": false,
|
||||
"pairsLastUpdated": 1522112372,
|
||||
"configCurrencyPairFormat": {
|
||||
"uppercase": false,
|
||||
"delimiter": "_"
|
||||
"Name": "OKEX",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"UseSandbox": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"HTTPTimeout": 15000000000,
|
||||
"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_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,
|
||||
"PairsLastUpdated": 1522112372,
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"requestCurrencyPairFormat": {
|
||||
"uppercase": false,
|
||||
"delimiter": "_"
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": false,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"bankAccounts": [
|
||||
"BankAccounts": [
|
||||
{
|
||||
"bankName": "",
|
||||
"bankAddress": "",
|
||||
|
||||
Reference in New Issue
Block a user