@imbingox/acex 0.4.0-beta.17 → 0.4.0-beta.18
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/CHANGELOG.md +6 -0
- package/docs/api.md +73 -2
- package/package.json +1 -1
- package/src/adapters/binance/adapter.ts +4 -1
- package/src/adapters/binance/market-catalog.ts +25 -20
- package/src/adapters/binance/private-adapter.ts +68 -53
- package/src/adapters/binance/rate-limit-topology.ts +257 -0
- package/src/adapters/binance/server-time.ts +20 -18
- package/src/client/runtime.ts +5 -1
- package/src/internal/rate-limiter/snapshot.ts +67 -0
- package/src/internal/rate-limiter/state.ts +98 -0
- package/src/internal/rate-limiter/topology.ts +123 -0
- package/src/internal/rate-limiter/types.ts +49 -0
- package/src/internal/rate-limiter/usage.ts +48 -0
- package/src/internal/rate-limiter.ts +792 -74
- package/src/types/shared.ts +196 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @imbingox/acex
|
|
2
2
|
|
|
3
|
+
## 0.4.0-beta.18
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3edefc1: Extend the public rate limiter SPI with optional topology plans, bucket reserve headroom, request priority, opaque reservations, and bucket-level snapshots. The default limiter now supports Binance REST topology registration, fixed-window bucket budget admission, cancel-priority reserve for Binance PAPI request weight, usage-header reconciliation, request-not-sent refunds, jittered bucket-level 429 fallback, and bucket-level 429/418 blocking while remaining backward compatible with existing custom `RateLimiter` implementations.
|
|
8
|
+
|
|
3
9
|
## 0.4.0-beta.17
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/docs/api.md
CHANGED
|
@@ -253,7 +253,7 @@ const client = createClient({
|
|
|
253
253
|
});
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
-
`clock` 只用于 outbound request / signing timestamp,不驱动 WebSocket freshness 的 received-at 时钟。需要自定义 REST 限流行为时可传 `rateLimiter`,否则使用默认
|
|
256
|
+
`clock` 只用于 outbound request / signing timestamp,不驱动 WebSocket freshness 的 received-at 时钟。需要自定义 REST 限流行为时可传 `rateLimiter`,否则使用默认 bucket-aware budget limiter:它会注册 Binance REST topology,在 `beforeRequest` 中按固定窗口和 `rateLimit.utilizationTarget`(默认 0.9)主动预扣预算,接近上限时 sleep 到下一窗口;Binance PAPI request-weight 桶为 `priority:"cancel"` 保留 headroom,撤单请求仍计入真实 weight 但可使用保留区;响应后的 Binance usage header 会回填校正 bucket 用量,429/418 block 也会落到对应 bucket,缺少 `Retry-After` 的 429 会冷却到窗口结束并带小 jitter。Binance `riskPollIntervalMs` 默认 5s,用于风险和 mark-to-market 仓位刷新;`privateReconcileIntervalMs` 默认 60s,用于账户余额、仓位和订单状态 REST 对账,显式传 `0` 可关闭 private reconcile,但不关闭 risk polling。`sandbox`、`logger`、`logLevel` 目前是预留位。
|
|
257
257
|
|
|
258
258
|
### 4.2 `start()` / `stop()`
|
|
259
259
|
|
|
@@ -599,6 +599,9 @@ interface CreateClientOptions {
|
|
|
599
599
|
sandbox?: boolean;
|
|
600
600
|
clock?: { now(): number };
|
|
601
601
|
rateLimiter?: RateLimiter;
|
|
602
|
+
rateLimit?: {
|
|
603
|
+
utilizationTarget?: number;
|
|
604
|
+
};
|
|
602
605
|
logger?: Logger;
|
|
603
606
|
logLevel?: "debug" | "info" | "warn" | "error";
|
|
604
607
|
market?: {
|
|
@@ -641,14 +644,77 @@ interface RateLimitUsage {
|
|
|
641
644
|
orderCount?: Record<string, number>;
|
|
642
645
|
}
|
|
643
646
|
|
|
647
|
+
type RateLimitPriority = "normal" | "cancel" | "risk" | (string & {});
|
|
648
|
+
type RateLimitBucketKind = "request_weight" | "orders" | (string & {});
|
|
649
|
+
type RateLimitScopeDimension = "venue" | "account" | "endpoint";
|
|
650
|
+
|
|
651
|
+
interface RateLimitBucketReserve {
|
|
652
|
+
priority: RateLimitPriority;
|
|
653
|
+
units: number;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
interface RateLimitBucketDescriptor {
|
|
657
|
+
id: string;
|
|
658
|
+
kind: RateLimitBucketKind;
|
|
659
|
+
limit: number;
|
|
660
|
+
intervalMs: number;
|
|
661
|
+
scope: readonly RateLimitScopeDimension[];
|
|
662
|
+
utilizationTarget?: number;
|
|
663
|
+
reserve?: RateLimitBucketReserve;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
interface RateLimitCost {
|
|
667
|
+
bucketId: string;
|
|
668
|
+
cost: number;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
interface RateLimitPlan {
|
|
672
|
+
id: string;
|
|
673
|
+
costs: readonly RateLimitCost[];
|
|
674
|
+
priority?: RateLimitPriority;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
interface RateLimitTopology {
|
|
678
|
+
id: string;
|
|
679
|
+
buckets: readonly RateLimitBucketDescriptor[];
|
|
680
|
+
plans: readonly RateLimitPlan[];
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
interface RateLimitReservation {
|
|
684
|
+
readonly __opaqueRateLimitReservation?: never;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
interface RateLimitTopologyRegistry {
|
|
688
|
+
registerRateLimitTopology(topology: RateLimitTopology): void;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
interface RateLimitBucketSnapshot {
|
|
692
|
+
bucketId: string;
|
|
693
|
+
kind: RateLimitBucketKind;
|
|
694
|
+
limit: number;
|
|
695
|
+
intervalMs: number;
|
|
696
|
+
utilizationTarget?: number;
|
|
697
|
+
reserve?: RateLimitBucketReserve;
|
|
698
|
+
used?: number;
|
|
699
|
+
windowStartMs?: number;
|
|
700
|
+
windowEndMs?: number;
|
|
701
|
+
blockedUntil?: number;
|
|
702
|
+
retryAfterMs?: number;
|
|
703
|
+
state: "ok" | "rate_limited" | "banned";
|
|
704
|
+
updatedAt?: number;
|
|
705
|
+
}
|
|
706
|
+
|
|
644
707
|
interface RateLimitRequestContext {
|
|
645
708
|
scope: RateLimitScope;
|
|
709
|
+
planId?: string;
|
|
710
|
+
priority?: RateLimitPriority;
|
|
646
711
|
}
|
|
647
712
|
|
|
648
713
|
interface RateLimitResponseContext {
|
|
649
714
|
status: number;
|
|
650
715
|
headers?: Headers;
|
|
651
716
|
usage?: RateLimitUsage;
|
|
717
|
+
reservation?: RateLimitReservation;
|
|
652
718
|
}
|
|
653
719
|
|
|
654
720
|
interface RateLimitTransportErrorContext {
|
|
@@ -656,6 +722,8 @@ interface RateLimitTransportErrorContext {
|
|
|
656
722
|
headers?: Headers;
|
|
657
723
|
retryAfterMs?: number;
|
|
658
724
|
usage?: RateLimitUsage;
|
|
725
|
+
reservation?: RateLimitReservation;
|
|
726
|
+
requestNotSent?: boolean;
|
|
659
727
|
}
|
|
660
728
|
|
|
661
729
|
interface RateLimitSnapshot {
|
|
@@ -665,10 +733,13 @@ interface RateLimitSnapshot {
|
|
|
665
733
|
retryAfterMs?: number;
|
|
666
734
|
state: "ok" | "rate_limited" | "banned";
|
|
667
735
|
updatedAt?: number;
|
|
736
|
+
buckets?: RateLimitBucketSnapshot[];
|
|
668
737
|
}
|
|
669
738
|
|
|
670
739
|
interface RateLimiter {
|
|
671
|
-
beforeRequest(
|
|
740
|
+
beforeRequest(
|
|
741
|
+
ctx: RateLimitRequestContext,
|
|
742
|
+
): Promise<RateLimitReservation | void> | RateLimitReservation | void;
|
|
672
743
|
afterResponse(
|
|
673
744
|
ctx: RateLimitRequestContext,
|
|
674
745
|
response: RateLimitResponseContext,
|
package/package.json
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type BinanceMarketDefinition,
|
|
18
18
|
loadBinanceMarkets,
|
|
19
19
|
} from "./market-catalog.ts";
|
|
20
|
+
import { registerBinanceRateLimitTopology } from "./rate-limit-topology.ts";
|
|
20
21
|
import { fetchBinanceServerTime } from "./server-time.ts";
|
|
21
22
|
import {
|
|
22
23
|
type BinanceStreamDescriptor,
|
|
@@ -61,7 +62,9 @@ export class BinanceMarketAdapter implements MarketAdapter {
|
|
|
61
62
|
private readonly options: {
|
|
62
63
|
readonly rateLimiter?: RateLimiter;
|
|
63
64
|
} = {},
|
|
64
|
-
) {
|
|
65
|
+
) {
|
|
66
|
+
registerBinanceRateLimitTopology(this.options.rateLimiter);
|
|
67
|
+
}
|
|
65
68
|
|
|
66
69
|
async loadMarkets(): Promise<MarketDefinition[]> {
|
|
67
70
|
const markets = await loadBinanceMarkets(fetch, {
|
|
@@ -11,8 +11,12 @@ import type {
|
|
|
11
11
|
RateLimitScope,
|
|
12
12
|
} from "../../types/index.ts";
|
|
13
13
|
import { parseBinanceRateLimitUsage } from "./rate-limit.ts";
|
|
14
|
+
import { getBinanceCatalogRateLimitPlanId } from "./rate-limit-topology.ts";
|
|
14
15
|
|
|
15
|
-
type FetchLike =
|
|
16
|
+
type FetchLike = (
|
|
17
|
+
input: string | URL | Request,
|
|
18
|
+
init?: RequestInit,
|
|
19
|
+
) => Promise<Response>;
|
|
16
20
|
|
|
17
21
|
export type BinanceMarketFamily = "spot" | "usdm" | "coinm";
|
|
18
22
|
|
|
@@ -240,8 +244,13 @@ async function requestCatalogJson<T>(
|
|
|
240
244
|
venue: "binance",
|
|
241
245
|
endpointKey,
|
|
242
246
|
};
|
|
247
|
+
const requestContext = {
|
|
248
|
+
scope,
|
|
249
|
+
planId: getBinanceCatalogRateLimitPlanId(endpointKey),
|
|
250
|
+
};
|
|
243
251
|
|
|
244
|
-
|
|
252
|
+
const reservation =
|
|
253
|
+
(await rateLimiter?.beforeRequest(requestContext)) ?? undefined;
|
|
245
254
|
|
|
246
255
|
try {
|
|
247
256
|
const response = await httpRequest<T>({
|
|
@@ -252,32 +261,28 @@ async function requestCatalogJson<T>(
|
|
|
252
261
|
jsonParseMode: "response",
|
|
253
262
|
retryPolicy: {
|
|
254
263
|
idempotent: true,
|
|
255
|
-
maxAttempts:
|
|
264
|
+
maxAttempts: 1,
|
|
256
265
|
},
|
|
257
266
|
messages: BINANCE_CATALOG_HTTP_MESSAGES,
|
|
258
267
|
});
|
|
259
268
|
|
|
260
|
-
await rateLimiter?.afterResponse(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
},
|
|
267
|
-
);
|
|
269
|
+
await rateLimiter?.afterResponse(requestContext, {
|
|
270
|
+
status: response.status,
|
|
271
|
+
headers: response.headers,
|
|
272
|
+
usage: parseBinanceRateLimitUsage(response.headers),
|
|
273
|
+
reservation,
|
|
274
|
+
});
|
|
268
275
|
|
|
269
276
|
return response.body;
|
|
270
277
|
} catch (error) {
|
|
271
278
|
if (isTransportError(error)) {
|
|
272
|
-
await rateLimiter?.onTransportError(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
},
|
|
280
|
-
);
|
|
279
|
+
await rateLimiter?.onTransportError(requestContext, {
|
|
280
|
+
status: error.status,
|
|
281
|
+
headers: error.headers,
|
|
282
|
+
retryAfterMs: error.retryAfterMs,
|
|
283
|
+
usage: parseBinanceRateLimitUsage(error.headers),
|
|
284
|
+
reservation,
|
|
285
|
+
});
|
|
281
286
|
}
|
|
282
287
|
|
|
283
288
|
throw error;
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
AccountCredentials,
|
|
12
12
|
PositionSide,
|
|
13
13
|
RateLimiter,
|
|
14
|
+
RateLimitPriority,
|
|
14
15
|
RateLimitScope,
|
|
15
16
|
TimeProvider,
|
|
16
17
|
VenueAccountCapabilities,
|
|
@@ -35,10 +36,17 @@ import type {
|
|
|
35
36
|
} from "../types.ts";
|
|
36
37
|
import { normalizeBinanceErrorCode } from "./error-codes.ts";
|
|
37
38
|
import { parseBinanceRateLimitUsage } from "./rate-limit.ts";
|
|
39
|
+
import {
|
|
40
|
+
getBinancePapiRateLimitPlanId,
|
|
41
|
+
registerBinanceRateLimitTopology,
|
|
42
|
+
} from "./rate-limit-topology.ts";
|
|
38
43
|
|
|
39
44
|
type TimerHandle = ReturnType<typeof setInterval>;
|
|
40
45
|
type SignedRequestMethod = "GET" | "POST" | "DELETE";
|
|
41
|
-
type FetchLike =
|
|
46
|
+
type FetchLike = (
|
|
47
|
+
input: string | URL | Request,
|
|
48
|
+
init?: RequestInit,
|
|
49
|
+
) => Promise<Response>;
|
|
42
50
|
|
|
43
51
|
interface BinancePapiBalance {
|
|
44
52
|
asset?: string;
|
|
@@ -172,18 +180,14 @@ const BINANCE_PAPI_WS_BASE_URL = "wss://fstream.binance.com/pm/ws";
|
|
|
172
180
|
const DEFAULT_RECV_WINDOW = 5_000;
|
|
173
181
|
const DEFAULT_HTTP_TIMEOUT_MS = 10_000;
|
|
174
182
|
const USDM_QUOTE_ASSETS = ["FDUSD", "USDC", "BUSD", "USDT"];
|
|
175
|
-
const
|
|
183
|
+
const SINGLE_ATTEMPT_IDEMPOTENT_POLICY: HttpRetryPolicy = {
|
|
176
184
|
idempotent: true,
|
|
177
|
-
maxAttempts:
|
|
185
|
+
maxAttempts: 1,
|
|
178
186
|
};
|
|
179
187
|
const NO_RETRY_POLICY: HttpRetryPolicy = {
|
|
180
188
|
idempotent: false,
|
|
181
189
|
maxAttempts: 1,
|
|
182
190
|
};
|
|
183
|
-
const LISTEN_KEY_KEEPALIVE_RETRY_POLICY: HttpRetryPolicy = {
|
|
184
|
-
idempotent: true,
|
|
185
|
-
maxAttempts: 3,
|
|
186
|
-
};
|
|
187
191
|
function getBinancePapiHttpMessages(timeoutMs: number): HttpClientMessages {
|
|
188
192
|
return {
|
|
189
193
|
http: ({ status, statusText, url, rawBody }) =>
|
|
@@ -674,7 +678,9 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
674
678
|
readonly signingClock?: TimeProvider;
|
|
675
679
|
readonly rateLimiter?: RateLimiter;
|
|
676
680
|
} = {},
|
|
677
|
-
) {
|
|
681
|
+
) {
|
|
682
|
+
registerBinanceRateLimitTopology(this.options.rateLimiter);
|
|
683
|
+
}
|
|
678
684
|
|
|
679
685
|
normalizeVenueErrorCode(code: string) {
|
|
680
686
|
return normalizeBinanceErrorCode(code);
|
|
@@ -692,7 +698,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
692
698
|
credentials,
|
|
693
699
|
accountOptions,
|
|
694
700
|
undefined,
|
|
695
|
-
|
|
701
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
696
702
|
),
|
|
697
703
|
this.signedRequest<BinancePapiAccount>(
|
|
698
704
|
"GET",
|
|
@@ -700,7 +706,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
700
706
|
credentials,
|
|
701
707
|
accountOptions,
|
|
702
708
|
undefined,
|
|
703
|
-
|
|
709
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
704
710
|
),
|
|
705
711
|
this.signedRequest<BinancePapiUmPosition[]>(
|
|
706
712
|
"GET",
|
|
@@ -708,7 +714,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
708
714
|
credentials,
|
|
709
715
|
accountOptions,
|
|
710
716
|
undefined,
|
|
711
|
-
|
|
717
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
712
718
|
),
|
|
713
719
|
]);
|
|
714
720
|
|
|
@@ -734,7 +740,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
734
740
|
credentials,
|
|
735
741
|
accountOptions,
|
|
736
742
|
undefined,
|
|
737
|
-
|
|
743
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
738
744
|
),
|
|
739
745
|
this.signedRequest<BinancePapiUmPosition[]>(
|
|
740
746
|
"GET",
|
|
@@ -742,7 +748,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
742
748
|
credentials,
|
|
743
749
|
accountOptions,
|
|
744
750
|
undefined,
|
|
745
|
-
|
|
751
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
746
752
|
),
|
|
747
753
|
]);
|
|
748
754
|
|
|
@@ -768,7 +774,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
768
774
|
credentials,
|
|
769
775
|
accountOptions,
|
|
770
776
|
undefined,
|
|
771
|
-
|
|
777
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
772
778
|
);
|
|
773
779
|
|
|
774
780
|
return {
|
|
@@ -797,7 +803,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
797
803
|
orderId: request.orderId,
|
|
798
804
|
origClientOrderId: request.clientOrderId,
|
|
799
805
|
},
|
|
800
|
-
|
|
806
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
801
807
|
);
|
|
802
808
|
|
|
803
809
|
return mapOpenOrder(response, receivedAt);
|
|
@@ -870,6 +876,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
870
876
|
origClientOrderId: request.clientOrderId,
|
|
871
877
|
},
|
|
872
878
|
NO_RETRY_POLICY,
|
|
879
|
+
"cancel",
|
|
873
880
|
);
|
|
874
881
|
|
|
875
882
|
const mapped = mapOpenOrder(response, receivedAt);
|
|
@@ -896,7 +903,8 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
896
903
|
{
|
|
897
904
|
symbol,
|
|
898
905
|
},
|
|
899
|
-
|
|
906
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
907
|
+
"cancel",
|
|
900
908
|
);
|
|
901
909
|
|
|
902
910
|
// Venue responds {code,msg}; returned updates are synthesized from the
|
|
@@ -911,6 +919,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
911
919
|
symbol,
|
|
912
920
|
},
|
|
913
921
|
NO_RETRY_POLICY,
|
|
922
|
+
"cancel",
|
|
914
923
|
);
|
|
915
924
|
|
|
916
925
|
if (response.code !== undefined && `${response.code}` !== "200") {
|
|
@@ -1216,10 +1225,18 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
1216
1225
|
accountOptions?: Record<string, unknown>,
|
|
1217
1226
|
queryParams?: Record<string, string | undefined>,
|
|
1218
1227
|
retryPolicy?: HttpRetryPolicy,
|
|
1228
|
+
priority?: RateLimitPriority,
|
|
1219
1229
|
): Promise<T> {
|
|
1220
1230
|
const { apiKey, secret } = requirePrivateCredentials(credentials);
|
|
1221
1231
|
const scope = this.rateLimitScope(method, path, accountOptions);
|
|
1222
|
-
|
|
1232
|
+
const requestContext = {
|
|
1233
|
+
scope,
|
|
1234
|
+
planId: getBinancePapiRateLimitPlanId(method, path, queryParams),
|
|
1235
|
+
priority,
|
|
1236
|
+
};
|
|
1237
|
+
const reservation =
|
|
1238
|
+
(await this.options.rateLimiter?.beforeRequest(requestContext)) ??
|
|
1239
|
+
undefined;
|
|
1223
1240
|
|
|
1224
1241
|
const params = new URLSearchParams();
|
|
1225
1242
|
for (const [key, value] of Object.entries(queryParams ?? {})) {
|
|
@@ -1258,27 +1275,23 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
1258
1275
|
messages: getBinancePapiHttpMessages(timeoutMs),
|
|
1259
1276
|
});
|
|
1260
1277
|
|
|
1261
|
-
await this.options.rateLimiter?.afterResponse(
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
},
|
|
1268
|
-
);
|
|
1278
|
+
await this.options.rateLimiter?.afterResponse(requestContext, {
|
|
1279
|
+
status: response.status,
|
|
1280
|
+
headers: response.headers,
|
|
1281
|
+
usage: parseBinanceRateLimitUsage(response.headers),
|
|
1282
|
+
reservation,
|
|
1283
|
+
});
|
|
1269
1284
|
|
|
1270
1285
|
return response.body;
|
|
1271
1286
|
} catch (error) {
|
|
1272
1287
|
if (isTransportError(error)) {
|
|
1273
|
-
await this.options.rateLimiter?.onTransportError(
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
},
|
|
1281
|
-
);
|
|
1288
|
+
await this.options.rateLimiter?.onTransportError(requestContext, {
|
|
1289
|
+
status: error.status,
|
|
1290
|
+
headers: error.headers,
|
|
1291
|
+
retryAfterMs: error.retryAfterMs,
|
|
1292
|
+
usage: parseBinanceRateLimitUsage(error.headers),
|
|
1293
|
+
reservation,
|
|
1294
|
+
});
|
|
1282
1295
|
}
|
|
1283
1296
|
|
|
1284
1297
|
throw error;
|
|
@@ -1312,7 +1325,7 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
1312
1325
|
"PUT",
|
|
1313
1326
|
credentials,
|
|
1314
1327
|
listenKey,
|
|
1315
|
-
|
|
1328
|
+
SINGLE_ATTEMPT_IDEMPOTENT_POLICY,
|
|
1316
1329
|
accountOptions,
|
|
1317
1330
|
);
|
|
1318
1331
|
}
|
|
@@ -1344,7 +1357,13 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
1344
1357
|
"/papi/v1/listenKey",
|
|
1345
1358
|
accountOptions,
|
|
1346
1359
|
);
|
|
1347
|
-
|
|
1360
|
+
const requestContext = {
|
|
1361
|
+
scope,
|
|
1362
|
+
planId: getBinancePapiRateLimitPlanId(method, "/papi/v1/listenKey"),
|
|
1363
|
+
};
|
|
1364
|
+
const reservation =
|
|
1365
|
+
(await this.options.rateLimiter?.beforeRequest(requestContext)) ??
|
|
1366
|
+
undefined;
|
|
1348
1367
|
|
|
1349
1368
|
const params = new URLSearchParams();
|
|
1350
1369
|
if (listenKey) {
|
|
@@ -1371,27 +1390,23 @@ export class BinancePrivateAdapter implements PrivateUserDataAdapter {
|
|
|
1371
1390
|
messages: getBinancePapiHttpMessages(timeoutMs),
|
|
1372
1391
|
});
|
|
1373
1392
|
|
|
1374
|
-
await this.options.rateLimiter?.afterResponse(
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
},
|
|
1381
|
-
);
|
|
1393
|
+
await this.options.rateLimiter?.afterResponse(requestContext, {
|
|
1394
|
+
status: response.status,
|
|
1395
|
+
headers: response.headers,
|
|
1396
|
+
usage: parseBinanceRateLimitUsage(response.headers),
|
|
1397
|
+
reservation,
|
|
1398
|
+
});
|
|
1382
1399
|
|
|
1383
1400
|
return response.body;
|
|
1384
1401
|
} catch (error) {
|
|
1385
1402
|
if (isTransportError(error)) {
|
|
1386
|
-
await this.options.rateLimiter?.onTransportError(
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
},
|
|
1394
|
-
);
|
|
1403
|
+
await this.options.rateLimiter?.onTransportError(requestContext, {
|
|
1404
|
+
status: error.status,
|
|
1405
|
+
headers: error.headers,
|
|
1406
|
+
retryAfterMs: error.retryAfterMs,
|
|
1407
|
+
usage: parseBinanceRateLimitUsage(error.headers),
|
|
1408
|
+
reservation,
|
|
1409
|
+
});
|
|
1395
1410
|
}
|
|
1396
1411
|
|
|
1397
1412
|
throw error;
|