This repository has been archived by the owner on Mar 23, 2023. It is now read-only.
forked from onuryilmaz/go-wex
-
Notifications
You must be signed in to change notification settings - Fork 0
/
trade.go
378 lines (293 loc) · 10.9 KB
/
trade.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
package wex
import (
"bytes"
"crypto/hmac"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
)
// TradeAPI allows to trade on the exchange and receive information about the account.
//
// To use this API, you need to create an API key. An API key can be created in your Profile in the API Keys section. After creating an API key you’ll receive a key and a secret.
// Note that the Secret can be received only during the first hour after the creation of the Key.
// API key information is used for authentication.
type TradeAPI struct {
API_KEY string
API_SECRET string
lastNonce int64
}
const tradeURL = "https://wex.nz/tapi"
// Auth provides API key and secret setting for Trade API
func (tapi *TradeAPI) Auth(key string, secret string) {
tapi.API_KEY = key
tapi.API_SECRET = secret
}
// GetInfo returns information about the user’s current balance, API-key privileges, the number of open orders and Server Time.
// To use this method you need a privilege of the key info.
func (tapi *TradeAPI) GetInfo() (AccountInfo, error) {
info := AccountInfo{}
err := tapi.call("getInfo", &info, nil)
if err == nil {
return info, nil
}
return info, err
}
// GetInfoAuth provides GetInfo capability with authorization
func (tapi *TradeAPI) GetInfoAuth(key string, secret string) (AccountInfo, error) {
tapi.Auth(key, secret)
return tapi.GetInfo()
}
// Trade provide method that can be used for creating orders and trading on the exchange.
// To use this method you need an API key privilege to trade.
//
// You can only create limit orders using this method, but you can emulate market orders using rate parameters. E.g. using rate=0.1 you can sell at the best market price.
//
// Each pair has a different limit on the minimum / maximum amounts, the minimum amount and the number of digits after the decimal point. All limitations can be obtained using the info method in PublicAPI.
func (tapi *TradeAPI) Trade(pair string, orderType string, rate float64, amount float64) (TradeResponse, error) {
tradeResponse := TradeResponse{}
orderParams := make(map[string]string, 4)
orderParams["pair"] = pair
orderParams["type"] = orderType
orderParams["rate"] = strconv.FormatFloat(rate, 'f', -1, 64)
orderParams["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
err := tapi.call("Trade", &tradeResponse, orderParams)
if err == nil {
return tradeResponse, nil
}
return tradeResponse, err
}
// TradeAuth provides Trade capability with authorization
func (tapi *TradeAPI) TradeAuth(key string, secret string, pair string, orderType string, rate float64, amount float64) (TradeResponse, error) {
tapi.Auth(key, secret)
return tapi.Trade(pair, orderType, rate, amount)
}
// ActiveOrders returns the list of your active orders. To use this method you need a privilege of the info key.
// If the order disappears from the list, it was either executed or canceled.
func (tapi *TradeAPI) ActiveOrders(pair string) (ActiveOrders, error) {
orderParams := make(map[string]string, 4)
orderParams["pair"] = pair
activeOrders := make(ActiveOrders, 0)
err := tapi.call("ActiveOrders", &activeOrders, orderParams)
if err == nil {
return activeOrders, nil
}
return activeOrders, err
}
// ActiveOrdersAuth provides ActiveOrders capability with authorization
func (tapi *TradeAPI) ActiveOrdersAuth(key string, secret string, pair string) (ActiveOrders, error) {
tapi.Auth(key, secret)
return tapi.ActiveOrders(pair)
}
// OrderInfo provides the information on particular order. To use this method you need a privilege of the info key.
func (tapi *TradeAPI) OrderInfo(orderID string) (OrderInfo, error) {
orderInfo := OrderInfo{}
orderParams := make(map[string]string, 1)
orderParams["order_id"] = orderID
err := tapi.call("OrderInfo", &orderInfo, orderParams)
if err == nil {
return orderInfo, nil
}
return orderInfo, err
}
// OrderInfoAuth provides OrderInfo capability with authorization
func (tapi *TradeAPI) OrderInfoAuth(key string, secret string, orderID string) (OrderInfo, error) {
tapi.Auth(key, secret)
return tapi.OrderInfo(orderID)
}
// CancelOrder provides method used for order cancellation. To use this method you need a privilege of the trade key.
func (tapi *TradeAPI) CancelOrder(orderID string) (CancelOrder, error) {
cancelReponse := CancelOrder{}
orderParams := make(map[string]string, 1)
orderParams["order_id"] = orderID
err := tapi.call("CancelOrder", &cancelReponse, orderParams)
if err == nil {
return cancelReponse, nil
}
return cancelReponse, err
}
// CancelOrderAuth provides CancelOrder capability with authorization
func (tapi *TradeAPI) CancelOrderAuth(key string, secret string, orderID string) (CancelOrder, error) {
tapi.Auth(key, secret)
return tapi.CancelOrder(orderID)
}
// TradeHistory returns trade history. To use this method you need a privilege of the info key.
func (tapi *TradeAPI) TradeHistory(filter HistoryFilter, pair string) (TradeHistory, error) {
tradeHistory := TradeHistory{}
historyParams := historyFilterParams(filter)
if pair != "" {
historyParams["pair"] = pair
}
err := tapi.call("TradeHistory", &tradeHistory, historyParams)
if err == nil {
return tradeHistory, nil
}
return tradeHistory, err
}
// TradeHistoryAuth provides TradeHistory capability with authorization
func (tapi *TradeAPI) TradeHistoryAuth(key string, secret string, filter HistoryFilter, pair string) (TradeHistory, error) {
tapi.Auth(key, secret)
return tapi.TradeHistory(filter, pair)
}
// TransactionHistory returns the history of transactions. To use this method you need a privilege of the info key.
func (tapi *TradeAPI) TransactionHistory(filter HistoryFilter) (TransactionHistory, error) {
transactionHistory := TransactionHistory{}
historyParams := historyFilterParams(filter)
err := tapi.call("TransHistory", &transactionHistory, historyParams)
if err == nil {
return transactionHistory, nil
}
return transactionHistory, err
}
// TransactionHistoryAuth provides TransactionHistory capability with authorization
func (tapi *TradeAPI) TransactionHistoryAuth(key string, secret string, filter HistoryFilter) (TransactionHistory, error) {
tapi.Auth(key, secret)
return tapi.TransactionHistory(filter)
}
// WithdrawCoin provides cryptocurrency withdrawals. You need to have the privilege of the Withdraw key to be able to use this method.
func (tapi *TradeAPI) WithdrawCoin(coinName string, amount float64, address string) (WithdrawCoin, error) {
response := WithdrawCoin{}
orderParams := make(map[string]string, 3)
orderParams["coinName"] = coinName
orderParams["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
orderParams["address"] = address
err := tapi.call("WithdrawCoin", &response, orderParams)
if err == nil {
return response, nil
}
return response, err
}
// WithdrawCoinAuth provides WithdrawCoin capability with authorization
func (tapi *TradeAPI) WithdrawCoinAuth(key string, secret string, coinName string, amount float64, address string) (WithdrawCoin, error) {
tapi.Auth(key, secret)
return tapi.WithdrawCoin(coinName, amount, address)
}
// CreateCoupon allows you to create Coupons. In order to use this method, you need the Coupon key privilege.
func (tapi *TradeAPI) CreateCoupon(currency string, amount float64) (CreateCoupon, error) {
response := CreateCoupon{}
params := make(map[string]string, 2)
params["currency"] = currency
params["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
err := tapi.call("CreateCoupon", &response, params)
if err == nil {
return response, nil
}
return response, err
}
// CreateCouponAuth provides CreateCoupon capability with authorization
func (tapi *TradeAPI) CreateCouponAuth(key string, secret string, currency string, amount float64) (CreateCoupon, error) {
tapi.Auth(key, secret)
return tapi.CreateCoupon(currency, amount)
}
// RedeemCoupon method is used to redeem coupons. In order to use this method, you need the Coupon key privilege.
func (tapi *TradeAPI) RedeemCoupon(coupon string) (RedeemCoupon, error) {
response := RedeemCoupon{}
params := make(map[string]string, 1)
params["coupon"] = coupon
err := tapi.call("RedeemCoupon", &response, params)
if err == nil {
return response, nil
}
return response, err
}
// RedeemCouponAuth provides RedeemCoupon capability with authorization
func (tapi *TradeAPI) RedeemCouponAuth(key string, secret string, coupon string) (RedeemCoupon, error) {
tapi.Auth(key, secret)
return tapi.RedeemCoupon(coupon)
}
func (tapi *TradeAPI) encodePostData(method string, params map[string]string) string {
nonce := time.Now().Unix()
if nonce <= tapi.lastNonce {
nonce = tapi.lastNonce + 1
}
tapi.lastNonce = nonce
result := fmt.Sprintf("method=%s&nonce=%d", method, nonce)
if len(params) > 0 {
v := url.Values{}
for key := range params {
v.Add(key, params[key])
}
result = result + "&" + v.Encode()
}
return result
}
func sign(secret string, payload string) string {
mac := hmac.New(sha512.New, []byte(secret))
mac.Write([]byte(payload))
return hex.EncodeToString(mac.Sum(nil))
}
func (tapi *TradeAPI) call(method string, v interface{}, params map[string]string) error {
postData := tapi.encodePostData(method, params)
req, err := http.NewRequest("POST", tradeURL, bytes.NewBufferString(postData))
if err != nil {
return err
}
req.Header.Add("Key", tapi.API_KEY)
req.Header.Add("Sign", sign(tapi.API_SECRET, postData))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(postData)))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
return marshalResponse(resp, v)
}
func marshalResponse(resp *http.Response, v interface{}) error {
// read the response
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
data := Response{}
if err = json.Unmarshal(bytes, &data); err != nil {
return err
}
if data.Success == 1 {
if err = json.Unmarshal(data.Return, &v); err != nil {
return err
}
} else {
return TradeError{data.Error}
}
return nil
}
// custom error type for server/trading errors
type TradeError struct {
msg string
}
func (e TradeError) Error() string {
return fmt.Sprintf("trading error: %v", e.msg)
}
// historyFilterParams creates map[string]string mapping of HistoryFilter
func historyFilterParams(filter HistoryFilter) map[string]string {
params := make(map[string]string, 0)
if filter.From > 0 {
params["from"] = strconv.Itoa(filter.From)
}
if filter.Count > 0 {
params["count"] = strconv.Itoa(filter.Count)
}
if filter.FromID > 0 {
params["from_id"] = strconv.Itoa(filter.FromID)
}
if filter.EndID > 0 {
params["end_id"] = strconv.Itoa(filter.EndID)
}
if filter.Order == "ASC" || filter.Order == "DESC" {
params["order"] = filter.Order
}
if filter.Since.Unix() > 0 {
params["since"] = strconv.FormatInt(filter.Since.Unix(), 10)
}
if filter.End.Unix() > 0 {
params["end"] = strconv.FormatInt(filter.End.Unix(), 10)
}
return params
}