/
websocket.go
173 lines (154 loc) · 5.44 KB
/
websocket.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package btcmarkets
import (
"fmt"
"strconv"
"time"
"github.com/gorilla/websocket"
"golang.org/x/net/context"
)
// BTCMWSTickEvent The tick event is published every time lastPrice,
// bestBid or bestAsk is updated for a market which is the result of
// orderbook changes or trade matches.
type BTCMWSTickEvent struct {
BestAsk string `json:"bestAsk"`
BestBid string `json:"bestBid"`
LastPrice string `json:"lastPrice"`
MarketID string `json:"marketId"`
MessageType string `json:"messageType"`
Timestamp string `json:"timestamp"`
Volume24h string `json:"volume24h"`
}
// BTCMWSTradeEvent In order to receive trade events please add trade to
// the list of channels when subscribing via WebSocket.
type BTCMWSTradeEvent struct {
MarketID string `json:"marketId"`
MessageType string `json:"messageType"`
Price string `json:"price"`
Side string `json:"side"`
Timestamp string `json:"timestamp"`
TradeID int64 `json:"tradeId"`
Volume string `json:"volume"`
}
// BTCMWSOrderbookEvent In order to receive orderbook events please add orderbook to
// the list of channels when subscribing via WebSocket. The current orderbook event represents
// the latest orderbook state and maximum 50 bids and asks are included in each event.
type BTCMWSOrderbookEvent struct {
Asks [][]string `json:"asks"`
Bids [][]string `json:"bids"`
MarketID string `json:"marketId"`
MessageType string `json:"messageType"`
Timestamp string `json:"timestamp"`
}
// BTCMWSOrderbookUpdateEvent In many cases, it's more appropriate to maintain a local copy of
// the exchange orderbook by receiving only updates instead of the entire orderbook.
type BTCMWSOrderbookUpdateEvent struct {
Asks [][]interface{} `json:"asks"`
Bids [][]interface{} `json:"bids"`
MarketID string `json:"marketId"`
MessageType string `json:"messageType"`
Snapshot bool `json:"snapshot"`
SnapshotID int64 `json:"snapshotId"`
Timestamp string `json:"timestamp"`
}
// BTCMWSTickResponse Response object res
type BTCMWSTickResponse struct {
BestAsk string `json:"bestAsk"`
BestBid string `json:"bestBid"`
High24h string `json:"high24h"`
LastPrice string `json:"lastPrice"`
Low24h string `json:"low24h"`
MarketID string `json:"marketId"`
MessageType string `json:"messageType"`
Price24h string `json:"price24h"`
SnapshotID int64 `json:"snapshotId"`
Timestamp string `json:"timestamp"`
Volume24h string `json:"volume24h"`
}
// BTCMWSHeartbeatEvent if you subscribe to heartbeat event
// then the server will send you a heartbeat event every 5 seconds.
// Note: Once a new subscription request is confirmed, a single heartbeat
// event is published to the client in order to confirm the connection working.
// This is regardless of requesting to subscribe to heartbeat channel.
type BTCMWSHeartbeatEvent struct {
Channels []struct {
MarketIds []string `json:"marketIds"`
Name string `json:"name"`
} `json:"channels"`
MessageType string `json:"messageType"`
}
// BTCMWSErrorEvent in case of errors, a message type of error is published.
// Authentication error
// Invalid input error
// Internal server error
// Throttle error
// Invalid Channel names
// Invalid MarketId
// Authenticate Error
type BTCMWSErrorEvent struct {
Code int64 `json:"code"`
Message string `json:"message"`
MessageType string `json:"messageType"`
}
// WSSubscribeMessage Subscribe message to initiate WebSocket Connection
type WSSubscribeMessage struct {
Channels []string `json:"channels"`
MarketIds []string `json:"marketIds"`
MessageType string `json:"messageType"`
Timestamp string `json:"timestamp"`
Key string `json:"key"`
Signature string `json:"signature"`
}
// WebSocketServiceOp WebSocket feed provides real-time market data covering
// orderbook updates, order life cycle and trades
type WebSocketServiceOp struct {
client *BTCMClient
}
// Subscribe returns a channel of bytes with messages from the websocket.
// The consumer of this method will need to handle the Implicit type Conversion
// of the bytes returned on the channel.
// This method needs to be called with a ContextWithCancel as first parameter to be able
// close the websocket and a SubscribeMessage to start receiving events for the
// specified channels and marketIds
func (ws *WebSocketServiceOp) Subscribe(ctx context.Context, m WSSubscribeMessage) (chan []byte, error) {
wsmessages := make(chan []byte)
c, _, err := websocket.DefaultDialer.Dial(ws.client.WSURL.String(), nil)
if err != nil {
fmt.Println("Error Dialing WebSocket Connection: ", err.Error())
return nil, err
}
if len(ws.client.apiKey) > 0 {
m.Key = ws.client.apiKey
}
if len(ws.client.privateKey) > 0 {
t := strconv.FormatInt(time.Now().UTC().UnixNano()/1000000, 10)
m.Timestamp = t
strToSign := "/users/self/subscribe" + "\n" + t
m.Signature = ws.client.signMessage(strToSign)
}
m.MessageType = "subscribe"
err = c.WriteJSON(m)
if err != nil {
fmt.Println(err.Error())
return nil, err
}
go func() {
defer c.Close()
defer close(wsmessages)
for {
select {
case <-ctx.Done():
return
default:
_, payload, err := c.ReadMessage()
if err != nil {
fmt.Println(err.Error())
wsmessages <- []byte(err.Error())
// return
// TODO: check if this code block should return on error
}
wsmessages <- payload
}
}
}()
return wsmessages, nil
}