-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.h
277 lines (231 loc) · 8.91 KB
/
client.h
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
// SPDX-License-Identifier: MIT
// Copyright (C) 2021 Marcelo Diop-Gonzalez
#ifndef CLIENT_H
#define CLIENT_H
#include <glib.h>
#include <jsmn/jsmn.h>
#include <openssl/hmac.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/queue.h>
#include "exchg/decimal.h"
#include "exchg/currency.h"
#include "exchg/exchg.h"
#include "json-helpers.h"
#include "net-backend.h"
#include "order-book.h"
struct work {
struct exchg_client *cl;
bool (*f)(struct exchg_client *, void *);
void *p;
LIST_ENTRY(work) list;
};
void remove_work(struct exchg_client *,
bool (*f)(struct exchg_client *, void *), void *p);
int queue_work(struct exchg_client *,
bool (*f)(struct exchg_client *, void *), void *p);
int queue_work_exclusive(struct exchg_client *,
bool (*f)(struct exchg_client *, void *), void *p);
void exchg_do_work(struct exchg_client *cl);
struct websocket;
struct http;
struct order_info {
struct exchg_order_info info;
void *req_private;
char private[];
};
static inline void *order_info_private(struct order_info *o) {
return o->private;
}
struct exchg_client {
enum exchg_id id;
const char *name;
struct exchg_context *ctx;
int (*get_pair_info)(struct exchg_client *cl);
int (*l2_subscribe)(struct exchg_client *cl, enum exchg_pair pair);
int (*get_balances)(struct exchg_client *cl, void *request_private);
int64_t (*place_order)(struct exchg_client *cl, const struct exchg_order *,
const struct exchg_place_order_opts *,
void *request_private);
int (*cancel_order)(struct exchg_client *cl, struct order_info *info);
int (*new_keypair)(struct exchg_client *cl,
const unsigned char *key, size_t len);
int (*priv_ws_connect)(struct exchg_client *cl);
bool (*priv_ws_online)(struct exchg_client *cl);
void (*destroy)(struct exchg_client *cl);
LIST_HEAD(websocket_list, websocket) websocket_list;
LIST_HEAD(http_list, http) http_list;
HMAC_CTX *hmac_ctx;
unsigned char *apikey_public;
size_t apikey_public_len;
char *password;
size_t password_len;
GHashTable *orders;
bool getting_info;
int get_info_error;
bool pair_info_current;
struct exchg_pair_info pair_info[EXCHG_NUM_PAIRS];
int l2_update_size;
struct exchg_l2_update update;
LIST_HEAD(work_list, work) work;
char private[];
};
int get_pair_info(struct exchg_client *cl);
static inline void *client_private(struct exchg_client *cl) {
return cl->private;
}
static inline void exchg_update_init(struct exchg_client *cl) {
cl->update.num_bids = 0;
cl->update.num_asks = 0;
}
int exchg_realloc_order_bufs(struct exchg_client *cl, int n);
static inline void order_err_cpy(struct exchg_order_info *info, const char *json, jsmntok_t *tok) {
if (tok)
json_strncpy(info->err, json, tok, EXCHG_ORDER_ERR_SIZE);
else
strncpy(info->err, "<unknown>", EXCHG_ORDER_ERR_SIZE);
}
struct order_info *__exchg_new_order(struct exchg_client *cl, const struct exchg_order *order,
const struct exchg_place_order_opts *opts,
void *req_private, size_t private_size, int64_t id);
struct order_info *exchg_new_order(struct exchg_client *cl, const struct exchg_order *order,
const struct exchg_place_order_opts *opts, void *req_private,
size_t private_size);
static inline bool order_status_done(enum exchg_order_status status) {
return status == EXCHG_ORDER_FINISHED || status == EXCHG_ORDER_CANCELED ||
status == EXCHG_ORDER_ERROR;
}
void order_info_free(struct exchg_client *cl, struct order_info *info);
void exchg_order_update(struct exchg_client *cl, struct order_info *oi,
enum exchg_order_status new_status, const decimal_t *new_size, bool cancel_failed);
struct order_info *exchg_order_lookup(struct exchg_client *cl, int64_t id);
__attribute__((format (printf, 3, 4)))
static inline void order_err_update(struct exchg_client *cl, struct order_info *oi,
const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(oi->info.err, EXCHG_ORDER_ERR_SIZE, fmt, ap);
exchg_order_update(cl, oi, EXCHG_ORDER_ERROR, NULL, false);
va_end(ap);
}
struct exchg_context {
struct exchg_options opts;
struct exchg_callbacks callbacks;
void *user;
struct exchg_client *clients[EXCHG_ALL_EXCHANGES];
struct order_book *books[EXCHG_NUM_PAIRS];
struct exchg_net_context *net_context;
bool running;
bool online;
};
static inline void exchg_l2_update(struct exchg_client *cl,
enum exchg_pair pair) {
struct exchg_context *ctx = cl->ctx;
struct exchg_l2_update *upd = &cl->update;
struct order_book *book = ctx->books[pair];
if (book)
order_book_add_update(book, upd);
if (ctx->callbacks.on_l2_update)
ctx->callbacks.on_l2_update(cl, pair, upd, ctx->user);
if (book)
order_book_update_finish(book, upd);
}
void exchg_data_disconnect(struct exchg_client *cl,
struct websocket *ws,
int num_pairs_gone,
enum exchg_pair *pairs_gone);
static inline void exchg_on_balances(struct exchg_client *cl,
const decimal_t balances[EXCHG_NUM_CCYS],
void *req_private) {
if (cl->ctx->callbacks.on_balances_recvd)
cl->ctx->callbacks.on_balances_recvd(cl, balances,
cl->ctx->user, req_private);
}
static inline void exchg_on_pair_info(struct exchg_client *cl) {
cl->pair_info_current = true;
cl->get_info_error = 0;
if (cl->ctx->callbacks.on_pair_info)
cl->ctx->callbacks.on_pair_info(cl, cl->ctx->user);
}
static inline void exchg_on_event(struct exchg_client *cl, int type) {
if (cl->ctx->callbacks.on_event)
cl->ctx->callbacks.on_event(cl, type, cl->ctx->user);
}
static inline void exchg_book_clear(struct exchg_client *cl, enum exchg_pair pair) {
struct order_book *book = cl->ctx->books[pair];
if (book)
order_book_clear(book, cl->id);
}
struct exchg_client *alloc_exchg_client(struct exchg_context *ctx,
enum exchg_id id, int l2_update_size, size_t private_size);
// complete in progress stuff first
// otherwise you can get a user after free in http_get callback
void free_exchg_client(struct exchg_client *cl);
struct exchg_websocket_ops {
int (*on_conn_established)(struct exchg_client *, struct websocket *);
int (*add_headers)(struct exchg_client *, struct websocket *);
int (*on_disconnect)(struct exchg_client *, struct websocket *,
int reconnect_seconds);
int (*recv)(struct exchg_client *, struct websocket *,
char *js, int num_toks, jsmntok_t *toks);
size_t conn_data_size;
};
bool websocket_disconnecting(struct websocket *);
bool websocket_established(struct websocket *);
const char *websocket_host(struct websocket *);
const char *websocket_path(struct websocket *);
const char *http_method(struct http *);
const char *http_host(struct http *);
const char *http_path(struct http *);
struct websocket *exchg_websocket_connect(struct exchg_client *cl,
const char *host, const char *path,
const struct exchg_websocket_ops *ops);
int websocket_printf(struct websocket *, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
int http_body_vsprintf(struct http *, const char *fmt, va_list args);
int http_body_sprintf(struct http *, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
char *http_body(struct http *http);
size_t http_body_len(struct http *http);
void http_body_trunc(struct http *http, size_t len);
void *websocket_private(struct websocket *);
void *http_private(struct http *);
void for_each_websocket(struct exchg_client *cl,
int (*func)(struct websocket *ws, void *private),
void *private);
int exchg_parse_info_on_established(struct exchg_client *cl,
struct http *, int status);
void exchg_parse_info_on_error(struct exchg_client *cl, struct http *,
const char *err);
void exchg_parse_info_on_closed(struct exchg_client *cl, struct http *);
int http_add_header(struct http *, const unsigned char *name,
const unsigned char *val, size_t len);
int websocket_add_header(struct websocket *, const unsigned char *name,
const unsigned char *val, size_t len);
struct exchg_http_ops {
int (*add_headers)(struct exchg_client *, struct http *);
// TODO: remove status param
int (*recv)(struct exchg_client *cl, struct http *, int status,
char *js, int num_toks, jsmntok_t *toks);
int (*on_established)(struct exchg_client *cl,
struct http *, int status);
void (*on_closed)(struct exchg_client *cl, struct http *);
void (*on_error)(struct exchg_client *cl, struct http *, const char *err);
void (*on_free)(struct exchg_client *cl, struct http *);
size_t conn_data_size;
};
struct http *exchg_http_get(const char *host, const char *path,
const struct exchg_http_ops *ops,
struct exchg_client *cl);
struct http *exchg_http_post(const char *host, const char *path,
const struct exchg_http_ops *ops,
struct exchg_client *cl);
struct http *exchg_http_delete(const char *host, const char *path,
const struct exchg_http_ops *ops,
struct exchg_client *cl);
void http_retry(struct http *);
void http_close(struct http *);
void websocket_close(struct websocket *);
void exchg_log(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
#endif