@cexyio/cexy 0.1.0-dev.2 → 0.1.0-dev.4
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.
- package/README.md +25 -5
- package/dist/index.cjs +73 -14
- package/dist/index.d.cts +109 -12
- package/dist/index.d.ts +109 -12
- package/dist/index.js +73 -15
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ The official TypeScript/JavaScript SDK for the [CEXY.io](https://cexy.io) REST a
|
|
|
5
5
|
- Typed models generated from the public OpenAPI spec ([cexy-api-spec](https://github.com/cexyio/cexy-api-spec)).
|
|
6
6
|
- Safe by default: retries with backoff, order placement that never duplicates (via `client_order_id`), a client-side rate limiter.
|
|
7
7
|
- A WebSocket client with heartbeat, reconnect and a live order book that applies the sync rules for you.
|
|
8
|
-
- ESM and CommonJS, Node
|
|
8
|
+
- ESM and CommonJS, Node 22+, zero runtime dependencies.
|
|
9
9
|
|
|
10
10
|
> **Status: 0.x.** The API is not yet frozen. It stays 0.x until the exchange ships HMAC request signing,
|
|
11
11
|
> which will change how credentials are sent.
|
|
@@ -15,7 +15,7 @@ The official TypeScript/JavaScript SDK for the [CEXY.io](https://cexy.io) REST a
|
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
npm install @cexyio/cexy@next
|
|
18
|
-
# Node only, optional: lets the WebSocket client send a User-Agent
|
|
18
|
+
# Node only, optional: lets the WebSocket client send a User-Agent
|
|
19
19
|
npm install ws
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -60,7 +60,23 @@ await cexy.trading.cancelAll({ symbol: "BTC/USDT" }); // { symbol: null } = ever
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
`cancelAll` requires `symbol`: the server treats a missing symbol as "every market", so the SDK makes
|
|
63
|
-
you say so with `{ symbol: null }`.
|
|
63
|
+
you say so with `{ symbol: null }`. An unknown symbol throws `NotFoundError`.
|
|
64
|
+
|
|
65
|
+
One call handles at most 500 orders and puts each in exactly one list: `cancelled`, `already_closed`
|
|
66
|
+
(it filled, was refused or was cancelled elsewhere first; not an error) or `failed`, with the reason in
|
|
67
|
+
`failures` (`INVALID_STATE` for an order still being placed). `has_more: true` means more orders remain.
|
|
68
|
+
To let the SDK repeat the call for you:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const res = await cexy.trading.cancelAll({ symbol: null, untilDone: true });
|
|
72
|
+
// res.stopped: "done" | "max_rounds" | "time_budget"; res.rounds: calls made
|
|
73
|
+
if (res.stopped !== "done" || res.failed.length) console.warn("left over:", res.failures);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`untilDone` repeats while `has_more` is true or a failure is `INVALID_STATE` / `SERVICE_UNAVAILABLE`.
|
|
77
|
+
After a call without progress it waits 1, 2, 4, 8, then 15 s, and it stops after `maxRounds` calls
|
|
78
|
+
(default 20) or before a wait would pass `timeBudgetMs` (default 120 000). The server allows 30 cancel-all
|
|
79
|
+
calls per minute per account; a 429 is retried after its Retry-After, which counts against the budget.
|
|
64
80
|
|
|
65
81
|
Give both `apiKey` and `apiSecret`, or neither: passing only one throws at construction.
|
|
66
82
|
|
|
@@ -110,7 +126,7 @@ Every API failure throws a `CexyApiError` (or a subclass) with `status`, `code`,
|
|
|
110
126
|
| `UnprocessableError` | 422: `INSUFFICIENT_FUNDS`, `MARKET_UNAVAILABLE`, ... |
|
|
111
127
|
| `RateLimitError` | 429, with `retryAfterMs` |
|
|
112
128
|
| `ServerError` | 5xx |
|
|
113
|
-
| `CexyApiError` | any code this SDK version does not know yet |
|
|
129
|
+
| `CexyApiError` | any code this SDK version does not know yet, and `UNEXPECTED_REDIRECT` (the server answered with a 3xx; set by the SDK, see `CLIENT_ERROR_CODES`) |
|
|
114
130
|
|
|
115
131
|
Local problems use `CexyConfigError`, `InvalidAmountError`, `CexyConnectionError` / `CexyTimeoutError`
|
|
116
132
|
and `OrderStateUnknownError`. `ErrorCode` is a union of the known codes plus `string`, because new codes
|
|
@@ -215,7 +231,7 @@ use public channels and poll REST for private state. If the session is revoked,
|
|
|
215
231
|
`authLost`; public channels keep working.
|
|
216
232
|
|
|
217
233
|
In Node the client uses the optional `ws` package when installed (so it can send the SDK User-Agent),
|
|
218
|
-
otherwise the global `WebSocket` (Node
|
|
234
|
+
otherwise the global `WebSocket` (Node, browsers).
|
|
219
235
|
|
|
220
236
|
## Browsers
|
|
221
237
|
|
|
@@ -229,6 +245,10 @@ endpoints from a server. Browsers do not let scripts set `User-Agent`, so the SD
|
|
|
229
245
|
- Credentials go only in the `X-API-Key` / `X-API-Secret` headers and only on private endpoints; never in URLs.
|
|
230
246
|
- Only `https://` base URLs and `wss://` WebSocket URLs are accepted. `allowInsecure: true` permits
|
|
231
247
|
`http://` / `ws://` solely for `localhost`, `127.0.0.1` or `::1` (local test servers).
|
|
248
|
+
- The SDK **never follows HTTP redirects**. A 3xx answer throws a `CexyApiError` with code
|
|
249
|
+
`UNEXPECTED_REDIRECT` (not retried), so credentials are never re-sent to another host and an order
|
|
250
|
+
is never re-posted to a redirect target. If you pass your own `fetch`, it must honour `redirect: "manual"`: one that follows redirects
|
|
251
|
+
anyway has already sent your credentials by the time the SDK notices.
|
|
232
252
|
- The SDK redacts the secret from `toString()`, `util.inspect`, `JSON.stringify` and error messages.
|
|
233
253
|
- Keep keys in environment variables or a secret manager, not in code.
|
|
234
254
|
|
package/dist/index.cjs
CHANGED
|
@@ -131,6 +131,7 @@ var KNOWN_CODES = /* @__PURE__ */ new Set([
|
|
|
131
131
|
"UNDER_MAINTENANCE",
|
|
132
132
|
"ENGINE_OVERLOADED"
|
|
133
133
|
]);
|
|
134
|
+
var CLIENT_ERROR_CODES = { UNEXPECTED_REDIRECT: "UNEXPECTED_REDIRECT" };
|
|
134
135
|
function isKnownErrorCode(code) {
|
|
135
136
|
return KNOWN_CODES.has(code);
|
|
136
137
|
}
|
|
@@ -394,7 +395,13 @@ var Transport = class {
|
|
|
394
395
|
let res;
|
|
395
396
|
let text;
|
|
396
397
|
try {
|
|
397
|
-
res = await this.config.fetch(url.toString(), {
|
|
398
|
+
res = await this.config.fetch(url.toString(), {
|
|
399
|
+
method: info.method,
|
|
400
|
+
headers,
|
|
401
|
+
body,
|
|
402
|
+
signal: ctrl.signal,
|
|
403
|
+
redirect: "manual"
|
|
404
|
+
});
|
|
398
405
|
text = await res.text();
|
|
399
406
|
} catch (err) {
|
|
400
407
|
if (opts.signal?.aborted) throw opts.signal.reason ?? err;
|
|
@@ -406,6 +413,14 @@ var Transport = class {
|
|
|
406
413
|
clearTimeout(timer);
|
|
407
414
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
408
415
|
}
|
|
416
|
+
if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400 || res.redirected) {
|
|
417
|
+
throw new CexyApiError({
|
|
418
|
+
status: res.status,
|
|
419
|
+
code: CLIENT_ERROR_CODES.UNEXPECTED_REDIRECT,
|
|
420
|
+
message: `${info.method} ${info.path}: the server answered with a redirect (HTTP ${res.status}); the SDK does not follow redirects. Check baseUrl.`,
|
|
421
|
+
retryable: false
|
|
422
|
+
});
|
|
423
|
+
}
|
|
409
424
|
this.config.limiter?.update(res.headers);
|
|
410
425
|
if (!res.ok) {
|
|
411
426
|
const parsed = safeJson(text);
|
|
@@ -776,6 +791,8 @@ var WalletResource = class extends Resource {
|
|
|
776
791
|
return this.data({ op: "deposit_address", query: params }, opts);
|
|
777
792
|
}
|
|
778
793
|
};
|
|
794
|
+
var CANCEL_RETRY_CODES = /* @__PURE__ */ new Set(["INVALID_STATE", "SERVICE_UNAVAILABLE"]);
|
|
795
|
+
var CANCEL_BACKOFF_S = [1, 2, 4, 8, 15];
|
|
779
796
|
var ORDER_AMOUNT_FIELDS = ["price", "quantity", "quote_quantity", "stop_price"];
|
|
780
797
|
var TradingResource = class extends Resource {
|
|
781
798
|
/** Open orders, optionally filtered by market/status. */
|
|
@@ -882,16 +899,6 @@ var TradingResource = class extends Resource {
|
|
|
882
899
|
}
|
|
883
900
|
}
|
|
884
901
|
}
|
|
885
|
-
/**
|
|
886
|
-
* Cancels every open order in one market: `cancelAll({ symbol: "BTC/USDT" })`.
|
|
887
|
-
* To cancel across ALL markets, pass `symbol: null` explicitly: `cancelAll({ symbol: null })`.
|
|
888
|
-
* Omitting `symbol` is an error, so an account-wide cancel never happens by accident
|
|
889
|
-
* (the server itself treats `{}` as every market).
|
|
890
|
-
*
|
|
891
|
-
* The server limits cancel-all to 30 calls per minute per account. It is naturally
|
|
892
|
-
* repeatable, so it is retried after network errors; a retry reports only what that retry
|
|
893
|
-
* cancelled.
|
|
894
|
-
*/
|
|
895
902
|
async cancelAll(params, opts) {
|
|
896
903
|
const hasSymbol = !!params && typeof params === "object" && Object.prototype.hasOwnProperty.call(params, "symbol");
|
|
897
904
|
const symbol = hasSymbol ? params.symbol : void 0;
|
|
@@ -899,12 +906,62 @@ var TradingResource = class extends Resource {
|
|
|
899
906
|
if (typeof symbol === "string" && symbol !== "") body = { symbol };
|
|
900
907
|
else if (hasSymbol && symbol === null) body = {};
|
|
901
908
|
else throw new CexyConfigError('cancelAll(): pass { symbol: "BASE/QUOTE" }, or { symbol: null } to cancel in every market');
|
|
902
|
-
|
|
909
|
+
const once = () => this.data({ op: "cancel_all", body }, opts);
|
|
910
|
+
if (params.untilDone !== true) return once();
|
|
911
|
+
const maxRounds = params.maxRounds ?? 20;
|
|
912
|
+
const budgetMs = params.timeBudgetMs ?? 12e4;
|
|
913
|
+
if (!(maxRounds >= 1)) throw new CexyConfigError("cancelAll(): maxRounds must be >= 1");
|
|
914
|
+
if (!(budgetMs > 0)) throw new CexyConfigError("cancelAll(): timeBudgetMs must be > 0");
|
|
915
|
+
const now = this.t.config.now ?? Date.now;
|
|
916
|
+
const start = now();
|
|
917
|
+
const state = /* @__PURE__ */ new Map();
|
|
918
|
+
let rounds = 0;
|
|
919
|
+
let idle = 0;
|
|
920
|
+
let last;
|
|
921
|
+
let stopped;
|
|
922
|
+
for (; ; ) {
|
|
923
|
+
last = await once();
|
|
924
|
+
rounds++;
|
|
925
|
+
const failures = last.failures ?? [];
|
|
926
|
+
for (const id of last.cancelled ?? []) state.set(id, { list: "cancelled" });
|
|
927
|
+
for (const id of last.already_closed ?? []) state.set(id, { list: "already_closed" });
|
|
928
|
+
for (const id of last.failed ?? []) {
|
|
929
|
+
const failure = failures.find((f) => f.order_id === id);
|
|
930
|
+
state.set(id, failure ? { list: "failed", failure } : { list: "failed" });
|
|
931
|
+
}
|
|
932
|
+
const progress = (last.cancelled?.length ?? 0) + (last.already_closed?.length ?? 0) > 0;
|
|
933
|
+
if (!last.has_more && !failures.some((f) => CANCEL_RETRY_CODES.has(f.code))) {
|
|
934
|
+
stopped = "done";
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
if (rounds >= maxRounds) {
|
|
938
|
+
stopped = "max_rounds";
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
let waitMs = 0;
|
|
942
|
+
if (progress) idle = 0;
|
|
943
|
+
else waitMs = (CANCEL_BACKOFF_S[Math.min(idle++, CANCEL_BACKOFF_S.length - 1)] ?? 15) * 1e3;
|
|
944
|
+
if (now() - start + waitMs >= budgetMs) {
|
|
945
|
+
stopped = "time_budget";
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
if (waitMs > 0) await this.t.config.sleep(waitMs, opts?.signal);
|
|
949
|
+
}
|
|
950
|
+
const pick = (list) => [...state].filter(([, v]) => v.list === list).map(([id]) => id);
|
|
951
|
+
return {
|
|
952
|
+
cancelled: pick("cancelled"),
|
|
953
|
+
already_closed: pick("already_closed"),
|
|
954
|
+
failed: pick("failed"),
|
|
955
|
+
failures: [...state.values()].flatMap((v) => v.list === "failed" && v.failure ? [v.failure] : []),
|
|
956
|
+
has_more: last.has_more,
|
|
957
|
+
rounds,
|
|
958
|
+
stopped
|
|
959
|
+
};
|
|
903
960
|
}
|
|
904
961
|
};
|
|
905
962
|
|
|
906
963
|
// src/version.ts
|
|
907
|
-
var VERSION = "0.1.0-dev.
|
|
964
|
+
var VERSION = "0.1.0-dev.4";
|
|
908
965
|
var USER_AGENT = `cexy-typescript/${VERSION}`;
|
|
909
966
|
|
|
910
967
|
// src/ws/emitter.ts
|
|
@@ -1664,7 +1721,7 @@ var CexyClient = class {
|
|
|
1664
1721
|
assertSecureUrl(parsed, "https:", "http:", options.allowInsecure === true, "baseUrl");
|
|
1665
1722
|
this.#allowInsecure = options.allowInsecure === true;
|
|
1666
1723
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1667
|
-
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node
|
|
1724
|
+
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 22+ required); pass options.fetch");
|
|
1668
1725
|
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
1669
1726
|
const maxRetries = options.maxRetries ?? 3;
|
|
1670
1727
|
if (!(timeoutMs > 0)) throw new CexyConfigError("timeoutMs must be > 0");
|
|
@@ -1685,6 +1742,7 @@ var CexyClient = class {
|
|
|
1685
1742
|
userAgent: canSetUserAgent() ? [USER_AGENT, options.userAgentSuffix].filter(Boolean).join(" ") : null,
|
|
1686
1743
|
sleep: sleep2,
|
|
1687
1744
|
random: options.random ?? Math.random,
|
|
1745
|
+
now: options.now ?? Date.now,
|
|
1688
1746
|
onRetry: options.onRetry
|
|
1689
1747
|
});
|
|
1690
1748
|
const t = this.#transport;
|
|
@@ -1758,6 +1816,7 @@ exports.AccountResource = AccountResource;
|
|
|
1758
1816
|
exports.ApiKeyAuthenticator = ApiKeyAuthenticator;
|
|
1759
1817
|
exports.AssetsResource = AssetsResource;
|
|
1760
1818
|
exports.AuthenticationError = AuthenticationError;
|
|
1819
|
+
exports.CLIENT_ERROR_CODES = CLIENT_ERROR_CODES;
|
|
1761
1820
|
exports.CexyApiError = CexyApiError;
|
|
1762
1821
|
exports.CexyClient = CexyClient;
|
|
1763
1822
|
exports.CexyConfigError = CexyConfigError;
|
package/dist/index.d.cts
CHANGED
|
@@ -163,6 +163,8 @@ interface TransportConfig {
|
|
|
163
163
|
userAgent: string | null;
|
|
164
164
|
sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
165
165
|
random: () => number;
|
|
166
|
+
/** Milliseconds clock (default `Date.now`); injectable for tests. */
|
|
167
|
+
now?: () => number;
|
|
166
168
|
onRetry?: ((info: RetryInfo) => void) | undefined;
|
|
167
169
|
}
|
|
168
170
|
interface CallSpec {
|
|
@@ -730,7 +732,11 @@ interface paths {
|
|
|
730
732
|
put?: never;
|
|
731
733
|
/**
|
|
732
734
|
* Cancels every open order, optionally within one market.
|
|
733
|
-
* @description Best-effort: a failure on one order does not stop the rest, and
|
|
735
|
+
* @description Best-effort: a failure on one order does not stop the rest, and every outcome is reported. A panic-button endpoint that stops at the first problem is worse than useless.
|
|
736
|
+
*
|
|
737
|
+
* An order still being placed when the call starts (status `pending`, for as long as its own placement request runs) is waited for, up to 500 ms per call in total: cancelled if it opens, reported in `already_closed` if it fills or is refused, and in `failed` with code `INVALID_STATE` if it is still being placed at the deadline. Orders placed after the call starts are not part of it. At most 500 orders per call; `has_more` says there are others.
|
|
738
|
+
*
|
|
739
|
+
* Limited to 30 calls a minute per account, on top of the general request limit.
|
|
734
740
|
*/
|
|
735
741
|
post: operations["cancel_all"];
|
|
736
742
|
delete?: never;
|
|
@@ -1037,12 +1043,31 @@ interface components {
|
|
|
1037
1043
|
/** @description Limit the cancellation to one market. Omitted or `null`, every market's open orders are cancelled. */
|
|
1038
1044
|
symbol?: string | null;
|
|
1039
1045
|
};
|
|
1040
|
-
/**
|
|
1046
|
+
/**
|
|
1047
|
+
* @description What a bulk cancellation achieved.
|
|
1048
|
+
*
|
|
1049
|
+
* Every order the call handled is in exactly one of `cancelled`, `already_closed` and `failed`.
|
|
1050
|
+
*/
|
|
1041
1051
|
CancelAllResponse: {
|
|
1052
|
+
/** @description Orders that closed on their own (filled, rejected, cancelled elsewhere) before this call reached them. Nothing was done to them, and they are not failures. */
|
|
1053
|
+
already_closed: string[];
|
|
1042
1054
|
/** @description Orders cancelled. */
|
|
1043
1055
|
cancelled: string[];
|
|
1044
|
-
/** @description Orders that could not be cancelled.
|
|
1056
|
+
/** @description Orders that could not be cancelled in this call. Check `failures` for why, then refresh or call again. */
|
|
1045
1057
|
failed: string[];
|
|
1058
|
+
/** @description Why each order in `failed` could not be cancelled. */
|
|
1059
|
+
failures: components["schemas"]["CancelFailureResponse"][];
|
|
1060
|
+
/** @description More open orders exist than one call handles (500). Call again. */
|
|
1061
|
+
has_more: boolean;
|
|
1062
|
+
};
|
|
1063
|
+
/** @description Why one order could not be cancelled. */
|
|
1064
|
+
CancelFailureResponse: {
|
|
1065
|
+
/** @description The error code of the attempt, as in any error response: e.g. `INVALID_STATE` for an order still being placed when the wait ran out, or `MARKET_UNAVAILABLE`. */
|
|
1066
|
+
code: string;
|
|
1067
|
+
/** @description Its message. */
|
|
1068
|
+
message: string;
|
|
1069
|
+
/** @description The order. */
|
|
1070
|
+
order_id: string;
|
|
1046
1071
|
};
|
|
1047
1072
|
/**
|
|
1048
1073
|
* @description Candle/kline intervals for market data.
|
|
@@ -2877,6 +2902,24 @@ interface operations {
|
|
|
2877
2902
|
};
|
|
2878
2903
|
};
|
|
2879
2904
|
};
|
|
2905
|
+
/** @description No such market */
|
|
2906
|
+
404: {
|
|
2907
|
+
headers: {
|
|
2908
|
+
[name: string]: unknown;
|
|
2909
|
+
};
|
|
2910
|
+
content: {
|
|
2911
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
2912
|
+
};
|
|
2913
|
+
};
|
|
2914
|
+
/** @description Over 30 calls a minute for this account; `details.retry_after_seconds` says when to retry */
|
|
2915
|
+
429: {
|
|
2916
|
+
headers: {
|
|
2917
|
+
[name: string]: unknown;
|
|
2918
|
+
};
|
|
2919
|
+
content: {
|
|
2920
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
2921
|
+
};
|
|
2922
|
+
};
|
|
2880
2923
|
};
|
|
2881
2924
|
};
|
|
2882
2925
|
order_history: {
|
|
@@ -3181,6 +3224,21 @@ type AssetNetwork = S["AssetNetworkResponse"];
|
|
|
3181
3224
|
type Balance = S["BalanceResponse"];
|
|
3182
3225
|
type CancelAllRequest = S["CancelAllRequest"];
|
|
3183
3226
|
type CancelAllResult = S["CancelAllResponse"];
|
|
3227
|
+
/** Why one order could not be cancelled (`CancelAllResult.failures`). */
|
|
3228
|
+
type CancelFailure = S["CancelFailureResponse"];
|
|
3229
|
+
/** Why a `cancelAll({ ..., untilDone: true })` loop ended. */
|
|
3230
|
+
type CancelAllStopReason = "done" | "max_rounds" | "time_budget";
|
|
3231
|
+
/**
|
|
3232
|
+
* Merged result of a `cancelAll({ ..., untilDone: true })` loop. Every order is in exactly one
|
|
3233
|
+
* of `cancelled`, `already_closed` and `failed`, in its latest state; `has_more` is the last
|
|
3234
|
+
* round's.
|
|
3235
|
+
*/
|
|
3236
|
+
interface CancelAllUntilDoneResult extends CancelAllResult {
|
|
3237
|
+
/** Number of cancel-all calls made. */
|
|
3238
|
+
rounds: number;
|
|
3239
|
+
/** `done`: nothing left to retry; otherwise the loop's limit that ended it. */
|
|
3240
|
+
stopped: CancelAllStopReason;
|
|
3241
|
+
}
|
|
3184
3242
|
type Candle = S["CandleResponse"];
|
|
3185
3243
|
type CandleInterval = S["CandleInterval"];
|
|
3186
3244
|
type DepositAddress = S["DepositAddressResponse"];
|
|
@@ -3344,6 +3402,14 @@ interface PlaceOrderResult extends PlaceOrderResponse {
|
|
|
3344
3402
|
*/
|
|
3345
3403
|
interface CancelAllParams {
|
|
3346
3404
|
symbol: string | null;
|
|
3405
|
+
/**
|
|
3406
|
+
* Repeat the call until nothing is left to retry (see `cancelAll`). Default false: one call.
|
|
3407
|
+
*/
|
|
3408
|
+
untilDone?: boolean;
|
|
3409
|
+
/** With `untilDone`: at most this many calls. Default 20. */
|
|
3410
|
+
maxRounds?: number;
|
|
3411
|
+
/** With `untilDone`: stop before a wait would take the loop to this many ms. Default 120000. */
|
|
3412
|
+
timeBudgetMs?: number;
|
|
3347
3413
|
}
|
|
3348
3414
|
declare class TradingResource extends Resource {
|
|
3349
3415
|
#private;
|
|
@@ -3380,12 +3446,27 @@ declare class TradingResource extends Resource {
|
|
|
3380
3446
|
* Cancels every open order in one market: `cancelAll({ symbol: "BTC/USDT" })`.
|
|
3381
3447
|
* To cancel across ALL markets, pass `symbol: null` explicitly: `cancelAll({ symbol: null })`.
|
|
3382
3448
|
* Omitting `symbol` is an error, so an account-wide cancel never happens by accident
|
|
3383
|
-
* (the server itself treats `{}` as every market).
|
|
3449
|
+
* (the server itself treats `{}` as every market). An unknown symbol throws `NotFoundError`.
|
|
3450
|
+
*
|
|
3451
|
+
* One call handles at most 500 orders. Every order it handled is in exactly one list:
|
|
3452
|
+
* `cancelled`; `already_closed` (it filled, was refused or was cancelled elsewhere first:
|
|
3453
|
+
* not an error); or `failed`, with the reason in `failures` (`INVALID_STATE` for an order
|
|
3454
|
+
* still being placed when the server's 500 ms wait ran out). `has_more` says more orders
|
|
3455
|
+
* remain: call again.
|
|
3456
|
+
*
|
|
3457
|
+
* With `untilDone: true` the SDK does that for you. It repeats while `has_more` is true or a
|
|
3458
|
+
* failure is `INVALID_STATE` / `SERVICE_UNAVAILABLE`. After a call that made no progress it
|
|
3459
|
+
* waits 1, 2, 4, 8 and then 15 s, starting over after any progress. It stops after
|
|
3460
|
+
* `maxRounds` calls or before a wait would pass `timeBudgetMs`, and returns the merged
|
|
3461
|
+
* result with `rounds` and `stopped`. Other failure codes are returned, never retried.
|
|
3384
3462
|
*
|
|
3385
|
-
* The server
|
|
3386
|
-
*
|
|
3387
|
-
*
|
|
3463
|
+
* The server allows 30 cancel-all calls per minute per account (a 429 is retried after its
|
|
3464
|
+
* Retry-After). The call is naturally repeatable and needs no Idempotency-Key, so it is
|
|
3465
|
+
* retried after network errors; a retry reports only what that retry did.
|
|
3388
3466
|
*/
|
|
3467
|
+
cancelAll(params: CancelAllParams & {
|
|
3468
|
+
untilDone: true;
|
|
3469
|
+
}, opts?: RequestOptions): Promise<CancelAllUntilDoneResult>;
|
|
3389
3470
|
cancelAll(params: CancelAllParams, opts?: RequestOptions): Promise<CancelAllResult>;
|
|
3390
3471
|
}
|
|
3391
3472
|
|
|
@@ -3484,7 +3565,16 @@ declare class RateLimitError extends CexyApiError {
|
|
|
3484
3565
|
/** 5xx */
|
|
3485
3566
|
declare class ServerError extends CexyApiError {
|
|
3486
3567
|
}
|
|
3487
|
-
/**
|
|
3568
|
+
/**
|
|
3569
|
+
* Codes the SDK itself sets on a `CexyApiError`; the API never sends them, so they are not in
|
|
3570
|
+
* errors.yaml and `isKnownErrorCode()` returns false for them.
|
|
3571
|
+
* - `UNEXPECTED_REDIRECT`: the server answered with a 3xx. The SDK never follows redirects (the
|
|
3572
|
+
* credentials would go to the redirect target); not retryable.
|
|
3573
|
+
*/
|
|
3574
|
+
declare const CLIENT_ERROR_CODES: {
|
|
3575
|
+
readonly UNEXPECTED_REDIRECT: "UNEXPECTED_REDIRECT";
|
|
3576
|
+
};
|
|
3577
|
+
/** True for codes listed in errors.yaml (server codes; see `CLIENT_ERROR_CODES` for the SDK's own). */
|
|
3488
3578
|
declare function isKnownErrorCode(code: string): code is KnownErrorCode;
|
|
3489
3579
|
/**
|
|
3490
3580
|
* Builds the right error subclass for an HTTP error response.
|
|
@@ -3844,7 +3934,12 @@ interface CexyClientOptions {
|
|
|
3844
3934
|
timeoutMs?: number;
|
|
3845
3935
|
/** Retries after the first attempt for retryable failures. Default 3. */
|
|
3846
3936
|
maxRetries?: number;
|
|
3847
|
-
/**
|
|
3937
|
+
/**
|
|
3938
|
+
* A `fetch` implementation. Default: the global `fetch` (Node 22+, browsers).
|
|
3939
|
+
* It must honour `redirect: "manual"` (the SDK never follows redirects). A fetch that follows
|
|
3940
|
+
* them anyway has already sent the credentials to the redirect target by the time the SDK sees
|
|
3941
|
+
* `response.redirected` and throws `UNEXPECTED_REDIRECT`.
|
|
3942
|
+
*/
|
|
3848
3943
|
fetch?: FetchLike;
|
|
3849
3944
|
/**
|
|
3850
3945
|
* Client-side rate limit in requests per minute, or `false` to disable. Default 100/min
|
|
@@ -3866,6 +3961,8 @@ interface CexyClientOptions {
|
|
|
3866
3961
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
3867
3962
|
/** @internal Deterministic jitter in tests. */
|
|
3868
3963
|
random?: () => number;
|
|
3964
|
+
/** @internal Replace the clock in tests (milliseconds). */
|
|
3965
|
+
now?: () => number;
|
|
3869
3966
|
}
|
|
3870
3967
|
/**
|
|
3871
3968
|
* CEXY.io REST client.
|
|
@@ -3925,11 +4022,11 @@ declare function isAmount(value: unknown): value is Amount;
|
|
|
3925
4022
|
declare function assertAmountFields(body: Record<string, unknown>, fields: readonly string[], context: string): void;
|
|
3926
4023
|
|
|
3927
4024
|
/** SDK version, kept in sync with package.json (a test enforces this). */
|
|
3928
|
-
declare const VERSION = "0.1.0-dev.
|
|
4025
|
+
declare const VERSION = "0.1.0-dev.4";
|
|
3929
4026
|
/** Default User-Agent product token. */
|
|
3930
|
-
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.
|
|
4027
|
+
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.4";
|
|
3931
4028
|
|
|
3932
4029
|
/** True for loopback hosts, the only ones where plain-text transport may be allowed. */
|
|
3933
4030
|
declare function isLocalHost(hostname: string): boolean;
|
|
3934
4031
|
|
|
3935
|
-
export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, JurisdictionBlockedError, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
|
|
4032
|
+
export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, CLIENT_ERROR_CODES, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type CancelAllStopReason, type CancelAllUntilDoneResult, type CancelFailure, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, JurisdictionBlockedError, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
|
package/dist/index.d.ts
CHANGED
|
@@ -163,6 +163,8 @@ interface TransportConfig {
|
|
|
163
163
|
userAgent: string | null;
|
|
164
164
|
sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
165
165
|
random: () => number;
|
|
166
|
+
/** Milliseconds clock (default `Date.now`); injectable for tests. */
|
|
167
|
+
now?: () => number;
|
|
166
168
|
onRetry?: ((info: RetryInfo) => void) | undefined;
|
|
167
169
|
}
|
|
168
170
|
interface CallSpec {
|
|
@@ -730,7 +732,11 @@ interface paths {
|
|
|
730
732
|
put?: never;
|
|
731
733
|
/**
|
|
732
734
|
* Cancels every open order, optionally within one market.
|
|
733
|
-
* @description Best-effort: a failure on one order does not stop the rest, and
|
|
735
|
+
* @description Best-effort: a failure on one order does not stop the rest, and every outcome is reported. A panic-button endpoint that stops at the first problem is worse than useless.
|
|
736
|
+
*
|
|
737
|
+
* An order still being placed when the call starts (status `pending`, for as long as its own placement request runs) is waited for, up to 500 ms per call in total: cancelled if it opens, reported in `already_closed` if it fills or is refused, and in `failed` with code `INVALID_STATE` if it is still being placed at the deadline. Orders placed after the call starts are not part of it. At most 500 orders per call; `has_more` says there are others.
|
|
738
|
+
*
|
|
739
|
+
* Limited to 30 calls a minute per account, on top of the general request limit.
|
|
734
740
|
*/
|
|
735
741
|
post: operations["cancel_all"];
|
|
736
742
|
delete?: never;
|
|
@@ -1037,12 +1043,31 @@ interface components {
|
|
|
1037
1043
|
/** @description Limit the cancellation to one market. Omitted or `null`, every market's open orders are cancelled. */
|
|
1038
1044
|
symbol?: string | null;
|
|
1039
1045
|
};
|
|
1040
|
-
/**
|
|
1046
|
+
/**
|
|
1047
|
+
* @description What a bulk cancellation achieved.
|
|
1048
|
+
*
|
|
1049
|
+
* Every order the call handled is in exactly one of `cancelled`, `already_closed` and `failed`.
|
|
1050
|
+
*/
|
|
1041
1051
|
CancelAllResponse: {
|
|
1052
|
+
/** @description Orders that closed on their own (filled, rejected, cancelled elsewhere) before this call reached them. Nothing was done to them, and they are not failures. */
|
|
1053
|
+
already_closed: string[];
|
|
1042
1054
|
/** @description Orders cancelled. */
|
|
1043
1055
|
cancelled: string[];
|
|
1044
|
-
/** @description Orders that could not be cancelled.
|
|
1056
|
+
/** @description Orders that could not be cancelled in this call. Check `failures` for why, then refresh or call again. */
|
|
1045
1057
|
failed: string[];
|
|
1058
|
+
/** @description Why each order in `failed` could not be cancelled. */
|
|
1059
|
+
failures: components["schemas"]["CancelFailureResponse"][];
|
|
1060
|
+
/** @description More open orders exist than one call handles (500). Call again. */
|
|
1061
|
+
has_more: boolean;
|
|
1062
|
+
};
|
|
1063
|
+
/** @description Why one order could not be cancelled. */
|
|
1064
|
+
CancelFailureResponse: {
|
|
1065
|
+
/** @description The error code of the attempt, as in any error response: e.g. `INVALID_STATE` for an order still being placed when the wait ran out, or `MARKET_UNAVAILABLE`. */
|
|
1066
|
+
code: string;
|
|
1067
|
+
/** @description Its message. */
|
|
1068
|
+
message: string;
|
|
1069
|
+
/** @description The order. */
|
|
1070
|
+
order_id: string;
|
|
1046
1071
|
};
|
|
1047
1072
|
/**
|
|
1048
1073
|
* @description Candle/kline intervals for market data.
|
|
@@ -2877,6 +2902,24 @@ interface operations {
|
|
|
2877
2902
|
};
|
|
2878
2903
|
};
|
|
2879
2904
|
};
|
|
2905
|
+
/** @description No such market */
|
|
2906
|
+
404: {
|
|
2907
|
+
headers: {
|
|
2908
|
+
[name: string]: unknown;
|
|
2909
|
+
};
|
|
2910
|
+
content: {
|
|
2911
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
2912
|
+
};
|
|
2913
|
+
};
|
|
2914
|
+
/** @description Over 30 calls a minute for this account; `details.retry_after_seconds` says when to retry */
|
|
2915
|
+
429: {
|
|
2916
|
+
headers: {
|
|
2917
|
+
[name: string]: unknown;
|
|
2918
|
+
};
|
|
2919
|
+
content: {
|
|
2920
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
2921
|
+
};
|
|
2922
|
+
};
|
|
2880
2923
|
};
|
|
2881
2924
|
};
|
|
2882
2925
|
order_history: {
|
|
@@ -3181,6 +3224,21 @@ type AssetNetwork = S["AssetNetworkResponse"];
|
|
|
3181
3224
|
type Balance = S["BalanceResponse"];
|
|
3182
3225
|
type CancelAllRequest = S["CancelAllRequest"];
|
|
3183
3226
|
type CancelAllResult = S["CancelAllResponse"];
|
|
3227
|
+
/** Why one order could not be cancelled (`CancelAllResult.failures`). */
|
|
3228
|
+
type CancelFailure = S["CancelFailureResponse"];
|
|
3229
|
+
/** Why a `cancelAll({ ..., untilDone: true })` loop ended. */
|
|
3230
|
+
type CancelAllStopReason = "done" | "max_rounds" | "time_budget";
|
|
3231
|
+
/**
|
|
3232
|
+
* Merged result of a `cancelAll({ ..., untilDone: true })` loop. Every order is in exactly one
|
|
3233
|
+
* of `cancelled`, `already_closed` and `failed`, in its latest state; `has_more` is the last
|
|
3234
|
+
* round's.
|
|
3235
|
+
*/
|
|
3236
|
+
interface CancelAllUntilDoneResult extends CancelAllResult {
|
|
3237
|
+
/** Number of cancel-all calls made. */
|
|
3238
|
+
rounds: number;
|
|
3239
|
+
/** `done`: nothing left to retry; otherwise the loop's limit that ended it. */
|
|
3240
|
+
stopped: CancelAllStopReason;
|
|
3241
|
+
}
|
|
3184
3242
|
type Candle = S["CandleResponse"];
|
|
3185
3243
|
type CandleInterval = S["CandleInterval"];
|
|
3186
3244
|
type DepositAddress = S["DepositAddressResponse"];
|
|
@@ -3344,6 +3402,14 @@ interface PlaceOrderResult extends PlaceOrderResponse {
|
|
|
3344
3402
|
*/
|
|
3345
3403
|
interface CancelAllParams {
|
|
3346
3404
|
symbol: string | null;
|
|
3405
|
+
/**
|
|
3406
|
+
* Repeat the call until nothing is left to retry (see `cancelAll`). Default false: one call.
|
|
3407
|
+
*/
|
|
3408
|
+
untilDone?: boolean;
|
|
3409
|
+
/** With `untilDone`: at most this many calls. Default 20. */
|
|
3410
|
+
maxRounds?: number;
|
|
3411
|
+
/** With `untilDone`: stop before a wait would take the loop to this many ms. Default 120000. */
|
|
3412
|
+
timeBudgetMs?: number;
|
|
3347
3413
|
}
|
|
3348
3414
|
declare class TradingResource extends Resource {
|
|
3349
3415
|
#private;
|
|
@@ -3380,12 +3446,27 @@ declare class TradingResource extends Resource {
|
|
|
3380
3446
|
* Cancels every open order in one market: `cancelAll({ symbol: "BTC/USDT" })`.
|
|
3381
3447
|
* To cancel across ALL markets, pass `symbol: null` explicitly: `cancelAll({ symbol: null })`.
|
|
3382
3448
|
* Omitting `symbol` is an error, so an account-wide cancel never happens by accident
|
|
3383
|
-
* (the server itself treats `{}` as every market).
|
|
3449
|
+
* (the server itself treats `{}` as every market). An unknown symbol throws `NotFoundError`.
|
|
3450
|
+
*
|
|
3451
|
+
* One call handles at most 500 orders. Every order it handled is in exactly one list:
|
|
3452
|
+
* `cancelled`; `already_closed` (it filled, was refused or was cancelled elsewhere first:
|
|
3453
|
+
* not an error); or `failed`, with the reason in `failures` (`INVALID_STATE` for an order
|
|
3454
|
+
* still being placed when the server's 500 ms wait ran out). `has_more` says more orders
|
|
3455
|
+
* remain: call again.
|
|
3456
|
+
*
|
|
3457
|
+
* With `untilDone: true` the SDK does that for you. It repeats while `has_more` is true or a
|
|
3458
|
+
* failure is `INVALID_STATE` / `SERVICE_UNAVAILABLE`. After a call that made no progress it
|
|
3459
|
+
* waits 1, 2, 4, 8 and then 15 s, starting over after any progress. It stops after
|
|
3460
|
+
* `maxRounds` calls or before a wait would pass `timeBudgetMs`, and returns the merged
|
|
3461
|
+
* result with `rounds` and `stopped`. Other failure codes are returned, never retried.
|
|
3384
3462
|
*
|
|
3385
|
-
* The server
|
|
3386
|
-
*
|
|
3387
|
-
*
|
|
3463
|
+
* The server allows 30 cancel-all calls per minute per account (a 429 is retried after its
|
|
3464
|
+
* Retry-After). The call is naturally repeatable and needs no Idempotency-Key, so it is
|
|
3465
|
+
* retried after network errors; a retry reports only what that retry did.
|
|
3388
3466
|
*/
|
|
3467
|
+
cancelAll(params: CancelAllParams & {
|
|
3468
|
+
untilDone: true;
|
|
3469
|
+
}, opts?: RequestOptions): Promise<CancelAllUntilDoneResult>;
|
|
3389
3470
|
cancelAll(params: CancelAllParams, opts?: RequestOptions): Promise<CancelAllResult>;
|
|
3390
3471
|
}
|
|
3391
3472
|
|
|
@@ -3484,7 +3565,16 @@ declare class RateLimitError extends CexyApiError {
|
|
|
3484
3565
|
/** 5xx */
|
|
3485
3566
|
declare class ServerError extends CexyApiError {
|
|
3486
3567
|
}
|
|
3487
|
-
/**
|
|
3568
|
+
/**
|
|
3569
|
+
* Codes the SDK itself sets on a `CexyApiError`; the API never sends them, so they are not in
|
|
3570
|
+
* errors.yaml and `isKnownErrorCode()` returns false for them.
|
|
3571
|
+
* - `UNEXPECTED_REDIRECT`: the server answered with a 3xx. The SDK never follows redirects (the
|
|
3572
|
+
* credentials would go to the redirect target); not retryable.
|
|
3573
|
+
*/
|
|
3574
|
+
declare const CLIENT_ERROR_CODES: {
|
|
3575
|
+
readonly UNEXPECTED_REDIRECT: "UNEXPECTED_REDIRECT";
|
|
3576
|
+
};
|
|
3577
|
+
/** True for codes listed in errors.yaml (server codes; see `CLIENT_ERROR_CODES` for the SDK's own). */
|
|
3488
3578
|
declare function isKnownErrorCode(code: string): code is KnownErrorCode;
|
|
3489
3579
|
/**
|
|
3490
3580
|
* Builds the right error subclass for an HTTP error response.
|
|
@@ -3844,7 +3934,12 @@ interface CexyClientOptions {
|
|
|
3844
3934
|
timeoutMs?: number;
|
|
3845
3935
|
/** Retries after the first attempt for retryable failures. Default 3. */
|
|
3846
3936
|
maxRetries?: number;
|
|
3847
|
-
/**
|
|
3937
|
+
/**
|
|
3938
|
+
* A `fetch` implementation. Default: the global `fetch` (Node 22+, browsers).
|
|
3939
|
+
* It must honour `redirect: "manual"` (the SDK never follows redirects). A fetch that follows
|
|
3940
|
+
* them anyway has already sent the credentials to the redirect target by the time the SDK sees
|
|
3941
|
+
* `response.redirected` and throws `UNEXPECTED_REDIRECT`.
|
|
3942
|
+
*/
|
|
3848
3943
|
fetch?: FetchLike;
|
|
3849
3944
|
/**
|
|
3850
3945
|
* Client-side rate limit in requests per minute, or `false` to disable. Default 100/min
|
|
@@ -3866,6 +3961,8 @@ interface CexyClientOptions {
|
|
|
3866
3961
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
3867
3962
|
/** @internal Deterministic jitter in tests. */
|
|
3868
3963
|
random?: () => number;
|
|
3964
|
+
/** @internal Replace the clock in tests (milliseconds). */
|
|
3965
|
+
now?: () => number;
|
|
3869
3966
|
}
|
|
3870
3967
|
/**
|
|
3871
3968
|
* CEXY.io REST client.
|
|
@@ -3925,11 +4022,11 @@ declare function isAmount(value: unknown): value is Amount;
|
|
|
3925
4022
|
declare function assertAmountFields(body: Record<string, unknown>, fields: readonly string[], context: string): void;
|
|
3926
4023
|
|
|
3927
4024
|
/** SDK version, kept in sync with package.json (a test enforces this). */
|
|
3928
|
-
declare const VERSION = "0.1.0-dev.
|
|
4025
|
+
declare const VERSION = "0.1.0-dev.4";
|
|
3929
4026
|
/** Default User-Agent product token. */
|
|
3930
|
-
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.
|
|
4027
|
+
declare const USER_AGENT = "cexy-typescript/0.1.0-dev.4";
|
|
3931
4028
|
|
|
3932
4029
|
/** True for loopback hosts, the only ones where plain-text transport may be allowed. */
|
|
3933
4030
|
declare function isLocalHost(hostname: string): boolean;
|
|
3934
4031
|
|
|
3935
|
-
export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, JurisdictionBlockedError, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
|
|
4032
|
+
export { AccountResource, type Amount, type ApiKey, ApiKeyAuthenticator, type ApiScope, type Asset, type AssetNetwork, AssetsResource, type AuthRequest, type AuthResult, type AuthenticatedFrame, AuthenticationError, type Authenticator, type Balance, type BalanceUpdatedEvent, type BookLevel, CLIENT_ERROR_CODES, type CancelAllParams, type CancelAllRequest, type CancelAllResult, type CancelAllStopReason, type CancelAllUntilDoneResult, type CancelFailure, type Candle, type CandleInterval, CexyApiError, type CexyApiErrorInit, CexyClient, type CexyClientOptions, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, type CexyWebSocketEvents, type CexyWebSocketOptions, type CloseInfo, ConflictError, type CursorParams, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, type Deposit, type DepositAddress, type DepositEvent, type DepositStatus, type ErrorBody, type ErrorCode, type ErrorFrame, type EventMap, type ExchangeConfig, type ExitPoolRequest, type ExitPoolResult, ExportsResource, type FeeSchedule, FeesResource, type FetchLike, type Fill, ForbiddenError, InvalidAmountError, type IterateOptions, type JoinPoolRequest, type JoinPoolResult, JurisdictionBlockedError, KNOWN_EVENT_TYPES, type KnownErrorCode, type LedgerEntry, type LedgerEntryKind, type LiquidityRole, type Listener, LiveOrderBook, type LiveOrderBookEvents, type LiveOrderBookOptions, type MaintenanceState, type Market, type MarketStatus, type MarketStatusEvent, MarketsResource, type Network, NetworksResource, NotFoundError, type Notification, type NotificationKind, OPERATIONS, type OperationAuth, type OperationId, type OperationInfo, type OperationScope, type Order, type OrderBook, type OrderBookUpdateData, type OrderBookUpdateEvent, type OrderEvent, type OrderSide, OrderStateUnknownError, type OrderStatus, type OrderType, PRIVATE_CHANNELS, type Page, type PlaceOrderRequest, type PlaceOrderResponse, type PlaceOrderResult, type PongFrame, type Pool, type PoolStatus, PoolsResource, type PublicTrade, type QueryOf, RateLimitError, RateLimiter, type RateLimiterOptions, type RateLimiterState, type ReconnectOptions, type RequestOptions, type ResyncReason, type RetryInfo, SUPPORTED_PROTOCOL_VERSION, ServerError, type ServerTime, type SessionRevokedData, type SessionRevokedEvent, type SnapshotSource, type SortDirection, type SubAccount, type SubscribeResult, type SubscribedFrame, type TickerUpdateEvent, type TimeInForce, type TradeNewEvent, TradingResource, type TriggerDirection, TypedEmitter, USER_AGENT, UnprocessableError, type UnsubscribedFrame, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, type WebSocketConstructor, type WebSocketLike, type WelcomeFrame, type Withdrawal, type WithdrawalAddress, type WithdrawalStatus, type WithdrawalUpdatedEvent, type WsEvent, type WsLogger, assertAmountFields, type components, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, type operations, paginate, type paths };
|
package/dist/index.js
CHANGED
|
@@ -129,6 +129,7 @@ var KNOWN_CODES = /* @__PURE__ */ new Set([
|
|
|
129
129
|
"UNDER_MAINTENANCE",
|
|
130
130
|
"ENGINE_OVERLOADED"
|
|
131
131
|
]);
|
|
132
|
+
var CLIENT_ERROR_CODES = { UNEXPECTED_REDIRECT: "UNEXPECTED_REDIRECT" };
|
|
132
133
|
function isKnownErrorCode(code) {
|
|
133
134
|
return KNOWN_CODES.has(code);
|
|
134
135
|
}
|
|
@@ -392,7 +393,13 @@ var Transport = class {
|
|
|
392
393
|
let res;
|
|
393
394
|
let text;
|
|
394
395
|
try {
|
|
395
|
-
res = await this.config.fetch(url.toString(), {
|
|
396
|
+
res = await this.config.fetch(url.toString(), {
|
|
397
|
+
method: info.method,
|
|
398
|
+
headers,
|
|
399
|
+
body,
|
|
400
|
+
signal: ctrl.signal,
|
|
401
|
+
redirect: "manual"
|
|
402
|
+
});
|
|
396
403
|
text = await res.text();
|
|
397
404
|
} catch (err) {
|
|
398
405
|
if (opts.signal?.aborted) throw opts.signal.reason ?? err;
|
|
@@ -404,6 +411,14 @@ var Transport = class {
|
|
|
404
411
|
clearTimeout(timer);
|
|
405
412
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
406
413
|
}
|
|
414
|
+
if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400 || res.redirected) {
|
|
415
|
+
throw new CexyApiError({
|
|
416
|
+
status: res.status,
|
|
417
|
+
code: CLIENT_ERROR_CODES.UNEXPECTED_REDIRECT,
|
|
418
|
+
message: `${info.method} ${info.path}: the server answered with a redirect (HTTP ${res.status}); the SDK does not follow redirects. Check baseUrl.`,
|
|
419
|
+
retryable: false
|
|
420
|
+
});
|
|
421
|
+
}
|
|
407
422
|
this.config.limiter?.update(res.headers);
|
|
408
423
|
if (!res.ok) {
|
|
409
424
|
const parsed = safeJson(text);
|
|
@@ -774,6 +789,8 @@ var WalletResource = class extends Resource {
|
|
|
774
789
|
return this.data({ op: "deposit_address", query: params }, opts);
|
|
775
790
|
}
|
|
776
791
|
};
|
|
792
|
+
var CANCEL_RETRY_CODES = /* @__PURE__ */ new Set(["INVALID_STATE", "SERVICE_UNAVAILABLE"]);
|
|
793
|
+
var CANCEL_BACKOFF_S = [1, 2, 4, 8, 15];
|
|
777
794
|
var ORDER_AMOUNT_FIELDS = ["price", "quantity", "quote_quantity", "stop_price"];
|
|
778
795
|
var TradingResource = class extends Resource {
|
|
779
796
|
/** Open orders, optionally filtered by market/status. */
|
|
@@ -880,16 +897,6 @@ var TradingResource = class extends Resource {
|
|
|
880
897
|
}
|
|
881
898
|
}
|
|
882
899
|
}
|
|
883
|
-
/**
|
|
884
|
-
* Cancels every open order in one market: `cancelAll({ symbol: "BTC/USDT" })`.
|
|
885
|
-
* To cancel across ALL markets, pass `symbol: null` explicitly: `cancelAll({ symbol: null })`.
|
|
886
|
-
* Omitting `symbol` is an error, so an account-wide cancel never happens by accident
|
|
887
|
-
* (the server itself treats `{}` as every market).
|
|
888
|
-
*
|
|
889
|
-
* The server limits cancel-all to 30 calls per minute per account. It is naturally
|
|
890
|
-
* repeatable, so it is retried after network errors; a retry reports only what that retry
|
|
891
|
-
* cancelled.
|
|
892
|
-
*/
|
|
893
900
|
async cancelAll(params, opts) {
|
|
894
901
|
const hasSymbol = !!params && typeof params === "object" && Object.prototype.hasOwnProperty.call(params, "symbol");
|
|
895
902
|
const symbol = hasSymbol ? params.symbol : void 0;
|
|
@@ -897,12 +904,62 @@ var TradingResource = class extends Resource {
|
|
|
897
904
|
if (typeof symbol === "string" && symbol !== "") body = { symbol };
|
|
898
905
|
else if (hasSymbol && symbol === null) body = {};
|
|
899
906
|
else throw new CexyConfigError('cancelAll(): pass { symbol: "BASE/QUOTE" }, or { symbol: null } to cancel in every market');
|
|
900
|
-
|
|
907
|
+
const once = () => this.data({ op: "cancel_all", body }, opts);
|
|
908
|
+
if (params.untilDone !== true) return once();
|
|
909
|
+
const maxRounds = params.maxRounds ?? 20;
|
|
910
|
+
const budgetMs = params.timeBudgetMs ?? 12e4;
|
|
911
|
+
if (!(maxRounds >= 1)) throw new CexyConfigError("cancelAll(): maxRounds must be >= 1");
|
|
912
|
+
if (!(budgetMs > 0)) throw new CexyConfigError("cancelAll(): timeBudgetMs must be > 0");
|
|
913
|
+
const now = this.t.config.now ?? Date.now;
|
|
914
|
+
const start = now();
|
|
915
|
+
const state = /* @__PURE__ */ new Map();
|
|
916
|
+
let rounds = 0;
|
|
917
|
+
let idle = 0;
|
|
918
|
+
let last;
|
|
919
|
+
let stopped;
|
|
920
|
+
for (; ; ) {
|
|
921
|
+
last = await once();
|
|
922
|
+
rounds++;
|
|
923
|
+
const failures = last.failures ?? [];
|
|
924
|
+
for (const id of last.cancelled ?? []) state.set(id, { list: "cancelled" });
|
|
925
|
+
for (const id of last.already_closed ?? []) state.set(id, { list: "already_closed" });
|
|
926
|
+
for (const id of last.failed ?? []) {
|
|
927
|
+
const failure = failures.find((f) => f.order_id === id);
|
|
928
|
+
state.set(id, failure ? { list: "failed", failure } : { list: "failed" });
|
|
929
|
+
}
|
|
930
|
+
const progress = (last.cancelled?.length ?? 0) + (last.already_closed?.length ?? 0) > 0;
|
|
931
|
+
if (!last.has_more && !failures.some((f) => CANCEL_RETRY_CODES.has(f.code))) {
|
|
932
|
+
stopped = "done";
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
if (rounds >= maxRounds) {
|
|
936
|
+
stopped = "max_rounds";
|
|
937
|
+
break;
|
|
938
|
+
}
|
|
939
|
+
let waitMs = 0;
|
|
940
|
+
if (progress) idle = 0;
|
|
941
|
+
else waitMs = (CANCEL_BACKOFF_S[Math.min(idle++, CANCEL_BACKOFF_S.length - 1)] ?? 15) * 1e3;
|
|
942
|
+
if (now() - start + waitMs >= budgetMs) {
|
|
943
|
+
stopped = "time_budget";
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
if (waitMs > 0) await this.t.config.sleep(waitMs, opts?.signal);
|
|
947
|
+
}
|
|
948
|
+
const pick = (list) => [...state].filter(([, v]) => v.list === list).map(([id]) => id);
|
|
949
|
+
return {
|
|
950
|
+
cancelled: pick("cancelled"),
|
|
951
|
+
already_closed: pick("already_closed"),
|
|
952
|
+
failed: pick("failed"),
|
|
953
|
+
failures: [...state.values()].flatMap((v) => v.list === "failed" && v.failure ? [v.failure] : []),
|
|
954
|
+
has_more: last.has_more,
|
|
955
|
+
rounds,
|
|
956
|
+
stopped
|
|
957
|
+
};
|
|
901
958
|
}
|
|
902
959
|
};
|
|
903
960
|
|
|
904
961
|
// src/version.ts
|
|
905
|
-
var VERSION = "0.1.0-dev.
|
|
962
|
+
var VERSION = "0.1.0-dev.4";
|
|
906
963
|
var USER_AGENT = `cexy-typescript/${VERSION}`;
|
|
907
964
|
|
|
908
965
|
// src/ws/emitter.ts
|
|
@@ -1662,7 +1719,7 @@ var CexyClient = class {
|
|
|
1662
1719
|
assertSecureUrl(parsed, "https:", "http:", options.allowInsecure === true, "baseUrl");
|
|
1663
1720
|
this.#allowInsecure = options.allowInsecure === true;
|
|
1664
1721
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1665
|
-
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node
|
|
1722
|
+
if (!fetchImpl) throw new CexyConfigError("no global fetch found (Node 22+ required); pass options.fetch");
|
|
1666
1723
|
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
1667
1724
|
const maxRetries = options.maxRetries ?? 3;
|
|
1668
1725
|
if (!(timeoutMs > 0)) throw new CexyConfigError("timeoutMs must be > 0");
|
|
@@ -1683,6 +1740,7 @@ var CexyClient = class {
|
|
|
1683
1740
|
userAgent: canSetUserAgent() ? [USER_AGENT, options.userAgentSuffix].filter(Boolean).join(" ") : null,
|
|
1684
1741
|
sleep: sleep2,
|
|
1685
1742
|
random: options.random ?? Math.random,
|
|
1743
|
+
now: options.now ?? Date.now,
|
|
1686
1744
|
onRetry: options.onRetry
|
|
1687
1745
|
});
|
|
1688
1746
|
const t = this.#transport;
|
|
@@ -1752,4 +1810,4 @@ function canSetUserAgent() {
|
|
|
1752
1810
|
return !(typeof g.window !== "undefined" && typeof g.window.document !== "undefined");
|
|
1753
1811
|
}
|
|
1754
1812
|
|
|
1755
|
-
export { AccountResource, ApiKeyAuthenticator, AssetsResource, AuthenticationError, CexyApiError, CexyClient, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, ConflictError, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, ExportsResource, FeesResource, ForbiddenError, InvalidAmountError, JurisdictionBlockedError, KNOWN_EVENT_TYPES, LiveOrderBook, MarketsResource, NetworksResource, NotFoundError, OPERATIONS, OrderStateUnknownError, PRIVATE_CHANNELS, PoolsResource, RateLimitError, RateLimiter, SUPPORTED_PROTOCOL_VERSION, ServerError, TradingResource, TypedEmitter, USER_AGENT, UnprocessableError, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, assertAmountFields, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, paginate };
|
|
1813
|
+
export { AccountResource, ApiKeyAuthenticator, AssetsResource, AuthenticationError, CLIENT_ERROR_CODES, CexyApiError, CexyClient, CexyConfigError, CexyConnectionError, CexyError, CexyTimeoutError, CexyWebSocket, CexyWebSocketError, ConflictError, DEFAULT_BASE_URL, DEFAULT_RPM_ANONYMOUS, DEFAULT_RPM_WITH_KEY, DEFAULT_WS_URL, ExportsResource, FeesResource, ForbiddenError, InvalidAmountError, JurisdictionBlockedError, KNOWN_EVENT_TYPES, LiveOrderBook, MarketsResource, NetworksResource, NotFoundError, OPERATIONS, OrderStateUnknownError, PRIVATE_CHANNELS, PoolsResource, RateLimitError, RateLimiter, SUPPORTED_PROTOCOL_VERSION, ServerError, TradingResource, TypedEmitter, USER_AGENT, UnprocessableError, VERSION, ValidationError, WS_BOOK_DEPTH, WalletResource, assertAmountFields, errorFromResponse, isAmount, isKnownErrorCode, isLocalHost, isRetryable, paginate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cexyio/cexy",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.4",
|
|
4
4
|
"description": "Official TypeScript/JavaScript SDK for the CEXY.io REST and WebSocket API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "CEXY.io",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"sideEffects": false,
|
|
45
45
|
"engines": {
|
|
46
|
-
"node": ">=
|
|
46
|
+
"node": ">=22"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"generate": "node scripts/generate.mjs",
|
|
@@ -65,16 +65,16 @@
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@eslint/js": "^
|
|
69
|
-
"@types/node": "^20.
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@types/node": "^22.20.4",
|
|
70
70
|
"@types/ws": "^8.18.0",
|
|
71
|
-
"eslint": "^
|
|
71
|
+
"eslint": "^10.11.0",
|
|
72
72
|
"openapi-typescript": "^7.13.0",
|
|
73
73
|
"tsup": "^8.5.0",
|
|
74
74
|
"typescript": "~5.9.3",
|
|
75
75
|
"typescript-eslint": "^8.70.0",
|
|
76
|
-
"vite": "^
|
|
77
|
-
"vitest": "^
|
|
76
|
+
"vite": "^8.3.0",
|
|
77
|
+
"vitest": "^5.0.2",
|
|
78
78
|
"ws": "^8.18.0"
|
|
79
79
|
},
|
|
80
80
|
"publishConfig": {
|