@gera2ld/lib-cex 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export * from './requesters';
package/dist/index.js ADDED
@@ -0,0 +1,351 @@
1
+ import _ from "node:process";
2
+ import { delay as A } from "es-toolkit";
3
+ import { createHmac as g } from "node:crypto";
4
+ _.env.CGI_DIR;
5
+ function d(n) {
6
+ const s = _.env[n];
7
+ if (!s)
8
+ throw new Error(`Missing environment variable: ${n}`);
9
+ return s;
10
+ }
11
+ const b = "=", K = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12
+ function q(n, s, t) {
13
+ let e = "", i = 0;
14
+ for (; i < n.length; ) {
15
+ const a = n[i++], o = n[i++], r = n[i++];
16
+ if (e += s[a >> 2], e += s[(a & 3) << 4 | (o || 0) >> 4], o == null ? e += b : e += s[(o & 15) << 2 | (r || 0) >> 6], r == null) {
17
+ e += b;
18
+ break;
19
+ }
20
+ e += s[r & 63];
21
+ }
22
+ return e;
23
+ }
24
+ function R(n) {
25
+ return q(n, K);
26
+ }
27
+ function x(n) {
28
+ return new TextEncoder().encode(n);
29
+ }
30
+ const j = {
31
+ maximumFractionDigits: 4,
32
+ exponentialThresholdLarge: 1e6,
33
+ exponentialThresholdSmall: 1e-6,
34
+ minimumSignificantDigits: 2,
35
+ maximumSignificantDigits: 8,
36
+ quantizationStep: 0,
37
+ keepTrailingZeros: !1
38
+ };
39
+ function N(n, s) {
40
+ if (n == null || n === "")
41
+ return "";
42
+ let t = +n;
43
+ if (Number.isNaN(t))
44
+ return "";
45
+ const e = {
46
+ ...j,
47
+ ...s
48
+ };
49
+ e.quantizationStep > 0 && (t = Math.round(t / e.quantizationStep) * e.quantizationStep);
50
+ const i = Math.abs(t), a = e.maximumSignificantDigits;
51
+ if (t !== 0 && (i >= e.exponentialThresholdLarge || i < e.exponentialThresholdSmall)) {
52
+ let c;
53
+ return e.keepTrailingZeros ? c = t.toExponential(Math.max(0, a - 1)) : (c = t.toPrecision(a), c = (+c).toExponential()), c;
54
+ }
55
+ const o = {
56
+ useGrouping: !1,
57
+ notation: "standard"
58
+ };
59
+ o.minimumSignificantDigits = e.minimumSignificantDigits, o.maximumSignificantDigits = a;
60
+ let r = new Intl.NumberFormat("en-US", o).format(t);
61
+ return !e.keepTrailingZeros && r.includes(".") && (r = r.replace(/\.?0+$/, "")), r;
62
+ }
63
+ const D = [
64
+ ["ms", 1],
65
+ ["s", 1e3],
66
+ ["m", 60],
67
+ ["h", 60],
68
+ ["d", 24],
69
+ ["mo", 30],
70
+ ["y", 365 / 30]
71
+ ];
72
+ function Y(n, s) {
73
+ const t = Math.sign(n);
74
+ n = Math.abs(n);
75
+ let e = "ms";
76
+ for (const [i, a] of D) {
77
+ if (n < a) break;
78
+ n /= a, e = i;
79
+ }
80
+ return `${t < 0 ? "-" : ""}${N(n, s) || ""}${e}`;
81
+ }
82
+ const T = {};
83
+ function G(n) {
84
+ return new URLSearchParams(
85
+ Object.entries(n || {}).filter(([, s]) => s != null).map(([s, t]) => [s, `${t}`])
86
+ );
87
+ }
88
+ function k(n) {
89
+ const s = Date.now();
90
+ return () => {
91
+ const t = Date.now() - s;
92
+ console.log(`${n}: ${Y(t)}`);
93
+ };
94
+ }
95
+ class P {
96
+ constructor(s, t, e = "spot") {
97
+ this.apiKey = s, this.apiSecret = t, this.marketType = e;
98
+ }
99
+ static defaultOpts = {
100
+ auth: !0,
101
+ checkResponse: !0,
102
+ method: "GET"
103
+ };
104
+ static create(s = "spot") {
105
+ const t = ["CRYPTO_BINANCE_API_KEY", "CRYPTO_BINANCE_API_SECRET"], e = d(t[0]), i = d(t[1]);
106
+ return new P(e, i, s);
107
+ }
108
+ requestToken = Promise.resolve();
109
+ async _request(s, t) {
110
+ const e = {
111
+ ...P.defaultOpts,
112
+ ...t
113
+ }, { auth: i, params: a, body: o } = e;
114
+ let { method: r } = e, c;
115
+ r === "GET" && o && (r = "POST", c = {
116
+ ...c,
117
+ "content-type": "application/x-www-form-urlencoded"
118
+ });
119
+ let p = a ? new URLSearchParams(a).toString() : "", u = o ? new URLSearchParams(o).toString() : null;
120
+ if (i) {
121
+ const y = `&timestamp=${Date.now()}`;
122
+ u ? u += y : p += y;
123
+ const $ = `&signature=${g("sha256", this.apiSecret).update(p + (u || "")).digest("hex")}`;
124
+ u ? u += $ : p += $, c = {
125
+ ...c,
126
+ "X-MBX-APIKEY": this.apiKey
127
+ };
128
+ }
129
+ p && (p = `?${p}`);
130
+ const l = {
131
+ spot: "api",
132
+ "future-u": "fapi",
133
+ "future-c": "dapi"
134
+ }[this.marketType], m = `https://${l}.binance.com/${l}${s}`, f = k(m), h = await fetch(m + p, {
135
+ ...T.options,
136
+ method: r,
137
+ headers: c,
138
+ body: u
139
+ }), S = await h.json();
140
+ if (f(), !h.ok) throw { status: h.status, data: S };
141
+ return S;
142
+ }
143
+ async _requestWithRetry(s, t) {
144
+ let e = new Error("Unknown error");
145
+ for (let i = 0; i < 3; i += 1) {
146
+ i > 0 && console.error(`Invalid timestamp, retry ${i}...`);
147
+ try {
148
+ return await this._request(s, t);
149
+ } catch (a) {
150
+ if (e = a, e?.status === 400 && e.data?.code === -1021)
151
+ continue;
152
+ break;
153
+ }
154
+ }
155
+ throw e;
156
+ }
157
+ request(s, t) {
158
+ const e = this.requestToken.then(
159
+ () => this._requestWithRetry(s, t)
160
+ );
161
+ return this.requestToken = e.catch(() => {
162
+ }).then(() => A(50)), e;
163
+ }
164
+ }
165
+ function w(n) {
166
+ if (n == null) return "";
167
+ if (Array.isArray(n)) return n.map(w).join("");
168
+ if (typeof n == "object") {
169
+ const s = n;
170
+ return Object.keys(s).sort().map((t) => s[t] == null ? "" : t + w(s[t])).join("");
171
+ }
172
+ return `${n}`;
173
+ }
174
+ function M(n, s, t) {
175
+ const { id: e, method: i, params: a, nonce: o } = t, r = w(a), c = i + e + n + r + o;
176
+ return g("sha256", s).update(c).digest("hex");
177
+ }
178
+ class E {
179
+ constructor(s, t) {
180
+ this.apiKey = s, this.apiSecret = t;
181
+ }
182
+ static defaultOpts = {
183
+ auth: !0,
184
+ checkResponse: !0,
185
+ method: "GET"
186
+ };
187
+ static create(s) {
188
+ let t = ["CRYPTO_CDC_API_KEY", "CRYPTO_CDC_API_SECRET"];
189
+ s && (t = t.map((a) => `${a}_${s}`));
190
+ const e = d(t[0]), i = d(t[1]);
191
+ return new E(e, i);
192
+ }
193
+ requestId = 0;
194
+ async request(s, t) {
195
+ const e = {
196
+ ...E.defaultOpts,
197
+ ...t
198
+ }, { auth: i, params: a } = e;
199
+ let { method: o } = e;
200
+ const r = {
201
+ method: s,
202
+ params: e.body
203
+ };
204
+ if (i) {
205
+ const { requestId: f } = this;
206
+ this.requestId += 1;
207
+ const h = Date.now();
208
+ Object.assign(r, {
209
+ id: f,
210
+ nonce: h,
211
+ api_key: this.apiKey
212
+ }), r.sig = M(this.apiKey, this.apiSecret, r);
213
+ }
214
+ const c = r.params || r.sig ? JSON.stringify(r) : null;
215
+ o === "GET" && c && (o = "POST");
216
+ const p = a ? "?" + new URLSearchParams(
217
+ Object.entries(a).map(([f, h]) => [f, `${h}`])
218
+ ).toString() : "", u = `https://api.crypto.com/v2/${s}${p}`, l = await fetch(u, {
219
+ ...T.options,
220
+ method: o,
221
+ headers: {
222
+ "Content-Type": "application/json"
223
+ },
224
+ body: c
225
+ }), m = await l.json();
226
+ if (!e.ignoreError && (!l.ok || m.code))
227
+ throw { status: l.status, data: m };
228
+ return m;
229
+ }
230
+ }
231
+ class O {
232
+ constructor(s, t) {
233
+ this.apiKey = s, this.apiSecret = t;
234
+ }
235
+ static defaultOpts = {
236
+ auth: !0,
237
+ checkResponse: !0
238
+ };
239
+ static create(s) {
240
+ let t = ["CRYPTO_GEMINI_API_KEY", "CRYPTO_GEMINI_API_SECRET"];
241
+ s && (t = t.map((a) => `${a}_${s}`));
242
+ const e = d(t[0]), i = d(t[1]);
243
+ return new O(e, i);
244
+ }
245
+ requestToken = {
246
+ public: Promise.resolve(),
247
+ private: Promise.resolve()
248
+ };
249
+ async doRequest(s, t) {
250
+ const { auth: e, params: i, body: a } = t;
251
+ let o = "";
252
+ const r = {
253
+ ...T.options
254
+ };
255
+ if (i && (o = new URLSearchParams(
256
+ Object.entries(i).map(([f, h]) => [f, `${h}`])
257
+ ).toString(), o && (o = `?${o}`)), e) {
258
+ const m = {
259
+ request: s,
260
+ nonce: Date.now() / 1e3,
261
+ ...a
262
+ }, f = R(x(JSON.stringify(m))), h = g("sha384", this.apiSecret).update(f).digest("hex");
263
+ Object.assign(r, {
264
+ method: "POST",
265
+ headers: {
266
+ "content-type": "text/plain",
267
+ "X-GEMINI-APIKEY": this.apiKey,
268
+ "X-GEMINI-PAYLOAD": f,
269
+ "X-GEMINI-SIGNATURE": h
270
+ }
271
+ });
272
+ }
273
+ const c = `https://api.gemini.com${s}`, p = k(c), u = await fetch(c + o, r), l = await u.json();
274
+ if (p(), !u.ok) throw { status: u.status, data: l };
275
+ return l;
276
+ }
277
+ request(s, t) {
278
+ const e = {
279
+ ...O.defaultOpts,
280
+ ...t
281
+ }, i = e.auth ? "private" : "public", a = this.requestToken[i], o = a.then(() => this.doRequest(s, e));
282
+ return this.requestToken[i] = Promise.allSettled([
283
+ o,
284
+ a.then(() => A(e.auth ? 200 : 1e3))
285
+ ]).then(() => {
286
+ }), o;
287
+ }
288
+ }
289
+ class I {
290
+ constructor(s, t, e) {
291
+ this.apiKey = s, this.apiSecret = t, this.apiPass = e;
292
+ }
293
+ static defaultOpts = {
294
+ auth: !0,
295
+ method: "GET",
296
+ checkResponse: !0
297
+ };
298
+ static create(s) {
299
+ let t = [
300
+ "CRYPTO_OKX_API_KEY",
301
+ "CRYPTO_OKX_API_SECRET",
302
+ "CRYPTO_OKX_API_PASS"
303
+ ];
304
+ s && (t = t.map((o) => `${o}_${s}`));
305
+ const e = d(t[0]), i = d(t[1]), a = d(t[2]);
306
+ return new I(e, i, a);
307
+ }
308
+ async request(s, t) {
309
+ const e = {
310
+ ...I.defaultOpts,
311
+ ...t
312
+ }, { auth: i, params: a, checkResponse: o } = e;
313
+ let { method: r } = e;
314
+ const c = (/* @__PURE__ */ new Date()).toISOString(), p = e.body == null ? null : JSON.stringify(e.body);
315
+ r === "GET" && p && (r = "POST");
316
+ let u = G(a).toString();
317
+ u && (u = `?${u}`);
318
+ const l = {
319
+ "Content-Type": "application/json"
320
+ };
321
+ if (i) {
322
+ const y = [c, r, s, u, p].filter(Boolean).join(""), C = R(
323
+ g("sha256", this.apiSecret).update(y).digest()
324
+ );
325
+ Object.assign(l, {
326
+ "OK-ACCESS-KEY": this.apiKey,
327
+ "OK-ACCESS-TIMESTAMP": c,
328
+ "OK-ACCESS-PASSPHRASE": this.apiPass,
329
+ "OK-ACCESS-SIGN": C
330
+ });
331
+ }
332
+ const m = `https://www.okx.com${s}`, f = k(m), h = await fetch(m + u, {
333
+ ...T.options,
334
+ method: r,
335
+ headers: l,
336
+ body: p
337
+ }), S = await h.json();
338
+ if (f(), !h.ok || o && S.code !== "0")
339
+ throw { status: h.status, data: S };
340
+ return S.data;
341
+ }
342
+ }
343
+ export {
344
+ P as BinanceRequester,
345
+ E as CDCRequester,
346
+ T as FetchConfig,
347
+ O as GeminiRequester,
348
+ I as OKXRequester,
349
+ G as buildSearchParams,
350
+ k as timeit
351
+ };
@@ -0,0 +1,14 @@
1
+ import type { IMarketType } from '@gera2ld/lib-trading';
2
+ import type { RequestOptions } from './types.ts';
3
+ export declare class BinanceRequester {
4
+ private apiKey;
5
+ private apiSecret;
6
+ marketType: IMarketType;
7
+ static defaultOpts: RequestOptions;
8
+ static create(marketType?: IMarketType): BinanceRequester;
9
+ private requestToken;
10
+ constructor(apiKey: string, apiSecret: string, marketType?: IMarketType);
11
+ private _request;
12
+ _requestWithRetry<T>(path: string, opts?: Partial<RequestOptions>): Promise<T>;
13
+ request<T = unknown>(path: string, opts?: Partial<RequestOptions>): Promise<T>;
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { RequestOptions } from './types.ts';
2
+ export declare class CDCRequester {
3
+ private apiKey;
4
+ private apiSecret;
5
+ static defaultOpts: RequestOptions;
6
+ static create(subaccount?: string): CDCRequester;
7
+ requestId: number;
8
+ constructor(apiKey: string, apiSecret: string);
9
+ request<T = unknown>(path: string, opts?: Partial<RequestOptions>): Promise<{
10
+ code: number;
11
+ result: T;
12
+ }>;
13
+ }
@@ -0,0 +1,11 @@
1
+ import type { RequestOptions } from './types.ts';
2
+ export declare class GeminiRequester {
3
+ private apiKey;
4
+ private apiSecret;
5
+ static defaultOpts: RequestOptions;
6
+ static create(subaccount?: string): GeminiRequester;
7
+ private requestToken;
8
+ constructor(apiKey: string, apiSecret: string);
9
+ private doRequest;
10
+ request<T = unknown>(path: string, opts?: Partial<RequestOptions>): Promise<T>;
11
+ }
@@ -0,0 +1,6 @@
1
+ export * from './binance';
2
+ export * from './crypto-com';
3
+ export * from './gemini';
4
+ export * from './okx';
5
+ export * from './types';
6
+ export * from './util';
@@ -0,0 +1,10 @@
1
+ import type { RequestOptions } from './types.ts';
2
+ export declare class OKXRequester {
3
+ private apiKey;
4
+ private apiSecret;
5
+ private apiPass;
6
+ static defaultOpts: RequestOptions;
7
+ static create(subaccount?: string): OKXRequester;
8
+ constructor(apiKey: string, apiSecret: string, apiPass: string);
9
+ request<T = unknown>(path: string, opts?: Partial<RequestOptions>): Promise<T>;
10
+ }
@@ -0,0 +1,8 @@
1
+ export interface RequestOptions {
2
+ auth: boolean;
3
+ checkResponse: boolean;
4
+ method?: string;
5
+ params?: Record<string, string | number | boolean>;
6
+ body?: object;
7
+ ignoreError?: boolean;
8
+ }
@@ -0,0 +1,6 @@
1
+ export declare const FetchConfig: {
2
+ /** Additional options passed to `fetch`. */
3
+ options?: any;
4
+ };
5
+ export declare function buildSearchParams(params: Record<string, unknown> | undefined): URLSearchParams;
6
+ export declare function timeit(label: string): () => void;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@gera2ld/lib-cex",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.js",
8
+ "types": "./dist/index.d.ts"
9
+ }
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public",
16
+ "registry": "https://registry.npmjs.org/"
17
+ },
18
+ "devDependencies": {
19
+ "@gera2ld/common": "^0.0.1",
20
+ "@gera2ld/common-node": "^0.0.1",
21
+ "@gera2ld/lib-trading": "^0.0.1"
22
+ },
23
+ "dependencies": {
24
+ "es-toolkit": "^1.41.0"
25
+ },
26
+ "scripts": {
27
+ "clean": "del-cli dist tsconfig.tsbuildinfo",
28
+ "build:types": "tsc",
29
+ "build:js": "vite build",
30
+ "build": "pnpm clean && pnpm /^build:/"
31
+ }
32
+ }