-
Notifications
You must be signed in to change notification settings - Fork 1
/
kraken.go
210 lines (171 loc) · 4.92 KB
/
kraken.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
package gokraken
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
const (
// APIBaseURL is the base URI of the Kraken API.
APIBaseURL = "https://api.kraken.com"
// APIKeyHeader is the header to send the API key to Kraken in.
APIKeyHeader = "API-Key"
// APINonceParam is the parameter to send the nonce to Kraken in.
APINonceParam = "nonce"
// APIPublicNamespace is the name of the public Kraken API namespace.
APIPublicNamespace = "public"
// APIPrivateNamespace is the name of the private Kraken API namespace.
APIPrivateNamespace = "private"
// APISignHeader is the header to send the API signature to Kraken in.
APISignHeader = "API-Sign"
// APIVersion is the current version number of the Kraken API.
APIVersion = 0
// ClientVersion is the current version of this client.
ClientVersion = "0.1.0"
)
var (
// UserAgent is the user agent string applied to all requests.
UserAgent = fmt.Sprintf("Go Kraken %s (github.com/danmrichards/gokraken)", ClientVersion)
)
// Kraken is responsible for all communication with the Kraken API.
type Kraken struct {
APIKey string
BaseURL string
HTTPClient *http.Client
Market *Market
UserData *UserData
Trading *Trading
Funding *Funding
PrivateKey string
}
// New returns a new Kraken object with a default HTTP client.
func New() *Kraken {
k := &Kraken{
HTTPClient: &http.Client{},
}
k.initServices()
return k
}
// NewWithAuth returns a new Kraken object with the authentication
// credentials required for private api endpoints.
func NewWithAuth(apiKey, privateKey string) *Kraken {
k := &Kraken{
APIKey: apiKey,
PrivateKey: privateKey,
HTTPClient: &http.Client{},
}
k.initServices()
return k
}
// NewWithHTTPClient returns a new Kraken object with a custom HTTP client.
func NewWithHTTPClient(httpClient *http.Client) *Kraken {
k := &Kraken{
HTTPClient: httpClient,
}
k.initServices()
return k
}
// Initialise services for the Kraken api client.
func (k *Kraken) initServices() {
k.Market = &Market{k}
k.UserData = &UserData{k}
k.Trading = &Trading{k}
k.Funding = &Funding{k}
}
// GetBaseURL returns the base URI of the Kraken API.
// If the BaseURL value is not set on the Kraken struct the constant APIBaseURL
// will be returned instead.
func (k *Kraken) GetBaseURL() string {
if k.BaseURL == "" {
return APIBaseURL
}
return k.BaseURL
}
// Call performs a request against the Kraken API.
func (k *Kraken) Call(req *http.Request) (res *Response, err error) {
apiResp, err := k.HTTPClient.Do(req)
if err != nil {
return
}
err = bindJSON(apiResp.Body, &res)
if err != nil {
return
}
return
}
// Dial prepares a request to send to the Kraken API.
func (k *Kraken) Dial(ctx context.Context, method, resource string, body url.Values) (req *http.Request, err error) {
req, err = http.NewRequest(method, k.ResourceURL(APIPublicNamespace, resource), strings.NewReader(body.Encode()))
if err != nil {
return
}
// Apply the context to the request to allow it to be cancelled.
req = req.WithContext(ctx)
req.Header.Add("User-Agent", UserAgent)
return
}
// DialWithAuth prepares an authenticated request to send to the Kraken API.
func (k *Kraken) DialWithAuth(ctx context.Context, method, resource string, body url.Values) (req *http.Request, err error) {
if k.APIKey == "" {
err = errors.New("missing or invalid api key")
return
}
if k.PrivateKey == "" {
err = errors.New("missing or invalid private key")
return
}
// Create an empty map if nil passed.
if body == nil {
body = url.Values{}
}
// Decode the private key.
secret, err := base64.StdEncoding.DecodeString(k.PrivateKey)
if err != nil {
err = fmt.Errorf("could not decode private key: %s", err)
return
}
// Create a unique nonce value for this request.
// https://www.kraken.com/en-gb/help/api#general-usage
nonce := time.Now().UnixNano()
body.Set(APINonceParam, strconv.FormatInt(nonce, 10))
// Generate the request.
req, err = http.NewRequest(method, k.ResourceURL(APIPrivateNamespace, resource), strings.NewReader(body.Encode()))
if err != nil {
return
}
// Apply the context to the request to allow it to be cancelled.
req = req.WithContext(ctx)
// Generate signature.
signature := &Signature{
APISecret: secret,
Data: body,
URI: k.ResourceURI(APIPrivateNamespace, resource),
}
// Apply headers.
req.Header.Add("User-Agent", UserAgent)
req.Header.Add(APIKeyHeader, k.APIKey)
req.Header.Add(APISignHeader, signature.Generate())
return
}
// ResourceURI returns the URI path for the given api resource.
func (k *Kraken) ResourceURI(namespace, resource string) string {
return fmt.Sprintf(
"/%d/%s/%s",
APIVersion,
namespace,
resource,
)
}
// ResourceURL returns a fully qualified URI for the given api resource.
func (k *Kraken) ResourceURL(namespace, resource string) string {
return fmt.Sprintf(
"%s%s",
k.GetBaseURL(),
k.ResourceURI(namespace, resource),
)
}