@imbingox/acex 0.1.0-beta.2 → 0.1.0-beta.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/package.json CHANGED
@@ -1,24 +1,46 @@
1
1
  {
2
2
  "name": "@imbingox/acex",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.4",
4
4
  "description": "Multi-exchange trading SDK for market data, account, and order management",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/imbingox/acex"
8
+ },
5
9
  "module": "index.ts",
6
10
  "type": "module",
7
11
  "exports": {
8
12
  ".": "./index.ts"
9
13
  },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
10
17
  "files": [
11
18
  "index.ts",
12
19
  "src/"
13
20
  ],
14
21
  "scripts": {
22
+ "changeset": "changeset",
23
+ "changeset:pre:enter:beta": "changeset pre enter beta && if [ -f .changeset/pre.json ]; then biome check --write .changeset/pre.json; fi",
24
+ "changeset:pre:exit": "changeset pre exit && if [ -f .changeset/pre.json ]; then biome check --write .changeset/pre.json; fi",
15
25
  "lint": "biome check .",
16
26
  "lint:fix": "biome check --write .",
27
+ "release": "changeset publish",
17
28
  "type-check": "tsc --noEmit",
18
- "test": "bun test"
29
+ "test": "bun test --max-concurrency=1",
30
+ "test:live:account": "bun run scripts/live-account-smoke.ts",
31
+ "test:live:account:smoke": "bun run scripts/live-account-smoke.ts --duration 10",
32
+ "test:live:account:soak": "bun run scripts/live-account-smoke.ts --duration 60 --disconnect-after 5",
33
+ "test:live:market": "bun run scripts/live-market-smoke.ts",
34
+ "test:live:market:smoke": "bun run scripts/live-market-smoke.ts --duration 10",
35
+ "test:live:market:soak": "bun run scripts/live-market-smoke.ts --duration 60 --disconnect-after 5 --disconnect-target perp",
36
+ "test:live:order": "bun run scripts/live-order-smoke.ts",
37
+ "test:live:order:smoke": "bun run scripts/live-order-smoke.ts --duration 10",
38
+ "test:live:order:soak": "bun run scripts/live-order-smoke.ts --duration 60 --disconnect-after 5",
39
+ "version-packages": "changeset version && files=\"package.json\"; if [ -f .changeset/pre.json ]; then files=\"$files .changeset/pre.json\"; fi; if [ -f CHANGELOG.md ]; then files=\"$files CHANGELOG.md\"; fi; biome check --write $files"
19
40
  },
20
41
  "devDependencies": {
21
42
  "@biomejs/biome": "^2.4.10",
43
+ "@changesets/cli": "^2.31.0",
22
44
  "@types/bun": "latest",
23
45
  "typescript": "^6.0.2"
24
46
  },
@@ -1,11 +1,14 @@
1
1
  import type { MarketDefinition } from "../../types/index.ts";
2
2
  import type {
3
+ FundingRateStreamCallbacks,
4
+ FundingRateStreamOptions,
3
5
  L1BookStreamCallbacks,
4
6
  L1BookStreamOptions,
5
7
  MarketAdapter,
6
8
  StreamHandle,
7
9
  } from "../types.ts";
8
10
  import { subscribeBinanceBookTicker } from "./book-ticker.ts";
11
+ import { subscribeBinanceMarkPrice } from "./mark-price.ts";
9
12
  import {
10
13
  type BinanceMarketDefinition,
11
14
  loadBinanceMarkets,
@@ -50,4 +53,28 @@ export class BinanceMarketAdapter implements MarketAdapter {
50
53
  options,
51
54
  );
52
55
  }
56
+
57
+ createFundingRateStream(
58
+ market: MarketDefinition,
59
+ callbacks: FundingRateStreamCallbacks,
60
+ options: FundingRateStreamOptions,
61
+ ): StreamHandle {
62
+ const binanceMarket = this.definitions.get(market.symbol);
63
+ if (!binanceMarket) {
64
+ throw new Error(`Unknown Binance market: ${market.symbol}`);
65
+ }
66
+
67
+ return subscribeBinanceMarkPrice(
68
+ binanceMarket,
69
+ {
70
+ onFundingRate(update) {
71
+ callbacks.onUpdate(update);
72
+ },
73
+ onFreshnessChange: callbacks.onFreshnessChange,
74
+ onDisconnected: callbacks.onDisconnected,
75
+ onError: callbacks.onError,
76
+ },
77
+ options,
78
+ );
79
+ }
53
80
  }
@@ -0,0 +1,126 @@
1
+ import { createManagedWebSocket } from "../../internal/managed-websocket.ts";
2
+ import type { BinanceMarketDefinition } from "./market-catalog.ts";
3
+
4
+ export interface BinanceFundingRateUpdate {
5
+ fundingRate: string;
6
+ nextFundingTime?: number;
7
+ markPrice?: string;
8
+ indexPrice?: string;
9
+ exchangeTs?: number;
10
+ receivedAt: number;
11
+ }
12
+
13
+ export interface BinanceMarkPriceSubscription {
14
+ readonly ready: Promise<void>;
15
+ close(): void;
16
+ }
17
+
18
+ export interface BinanceMarkPriceCallbacks {
19
+ onFundingRate(update: BinanceFundingRateUpdate): void;
20
+ onFreshnessChange(
21
+ freshness: "fresh" | "stale",
22
+ reason?: "heartbeat_timeout",
23
+ ): void;
24
+ onDisconnected(): void;
25
+ onError?(error: Error): void;
26
+ }
27
+
28
+ export interface BinanceMarkPriceOptions {
29
+ initialMessageTimeoutMs: number;
30
+ staleAfterMs: number;
31
+ reconnectDelayMs: number;
32
+ reconnectMaxDelayMs: number;
33
+ now?: () => number;
34
+ }
35
+
36
+ interface BinanceMarkPriceMessage {
37
+ e?: string;
38
+ E?: number;
39
+ s?: string;
40
+ p?: string;
41
+ i?: string;
42
+ r?: string;
43
+ T?: number;
44
+ }
45
+
46
+ const BINANCE_USDM_WS_BASE_URL = "wss://fstream.binance.com/ws";
47
+ const BINANCE_COINM_WS_BASE_URL = "wss://dstream.binance.com/ws";
48
+
49
+ function getWsBaseUrl(market: BinanceMarketDefinition): string {
50
+ switch (market.family) {
51
+ case "usdm":
52
+ return BINANCE_USDM_WS_BASE_URL;
53
+ case "coinm":
54
+ return BINANCE_COINM_WS_BASE_URL;
55
+ case "spot":
56
+ throw new Error(
57
+ `Funding rate is not supported for spot market: ${market.symbol}`,
58
+ );
59
+ }
60
+ }
61
+
62
+ function buildMarkPriceUrl(market: BinanceMarketDefinition): string {
63
+ return `${getWsBaseUrl(market)}/${market.id.toLowerCase()}@markPrice@1s`;
64
+ }
65
+
66
+ function parseMarkPriceMessage(
67
+ data: string,
68
+ ): BinanceMarkPriceMessage | undefined {
69
+ const parsed = JSON.parse(data) as BinanceMarkPriceMessage;
70
+ if (!parsed.r) {
71
+ return undefined;
72
+ }
73
+
74
+ return parsed;
75
+ }
76
+
77
+ export function subscribeBinanceMarkPrice(
78
+ market: BinanceMarketDefinition,
79
+ callbacks: BinanceMarkPriceCallbacks,
80
+ options: BinanceMarkPriceOptions,
81
+ ): BinanceMarkPriceSubscription {
82
+ const session = createManagedWebSocket<BinanceMarkPriceMessage>({
83
+ url: buildMarkPriceUrl(market),
84
+ initialMessageTimeoutMs: options.initialMessageTimeoutMs,
85
+ now: options.now,
86
+ messageWatchdog: {
87
+ staleAfterMs: options.staleAfterMs,
88
+ onStale() {
89
+ callbacks.onFreshnessChange("stale", "heartbeat_timeout");
90
+ },
91
+ },
92
+ reconnect: {
93
+ initialDelayMs: options.reconnectDelayMs,
94
+ maxDelayMs: options.reconnectMaxDelayMs,
95
+ },
96
+ parseMessage: parseMarkPriceMessage,
97
+ onMessage(message, receivedAt) {
98
+ if (!message.r) {
99
+ return;
100
+ }
101
+
102
+ callbacks.onFundingRate({
103
+ fundingRate: message.r,
104
+ nextFundingTime: message.T,
105
+ markPrice: message.p,
106
+ indexPrice: message.i,
107
+ exchangeTs: message.E,
108
+ receivedAt,
109
+ });
110
+ callbacks.onFreshnessChange("fresh");
111
+ },
112
+ onUnexpectedClose() {
113
+ callbacks.onDisconnected();
114
+ },
115
+ onError() {
116
+ callbacks.onError?.(new Error(`WebSocket error for ${market.symbol}`));
117
+ },
118
+ });
119
+
120
+ return {
121
+ ready: session.ready,
122
+ close() {
123
+ session.close();
124
+ },
125
+ };
126
+ }