@pear-protocol/exchanges-sdk 0.0.10 → 0.0.12

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.
Files changed (60) hide show
  1. package/README.md +59 -42
  2. package/dist/account/account-manager.d.ts +5 -11
  3. package/dist/account/account-manager.js +22 -60
  4. package/dist/account/exchange-account.d.ts +8 -10
  5. package/dist/account/exchange-account.js +30 -57
  6. package/dist/account/exchanges/base.d.ts +13 -15
  7. package/dist/account/exchanges/base.js +5 -27
  8. package/dist/account/exchanges/binance/asset-tracker.d.ts +25 -0
  9. package/dist/account/exchanges/binance/asset-tracker.js +38 -0
  10. package/dist/account/exchanges/binance/mapper.d.ts +4 -3
  11. package/dist/account/exchanges/binance/mapper.js +0 -1
  12. package/dist/account/exchanges/binance/orchestrator.d.ts +9 -12
  13. package/dist/account/exchanges/binance/orchestrator.js +61 -183
  14. package/dist/account/exchanges/binance/rest.d.ts +5 -4
  15. package/dist/account/exchanges/binance/rest.js +4 -20
  16. package/dist/account/exchanges/binance/state-handler.d.ts +32 -0
  17. package/dist/account/exchanges/binance/state-handler.js +113 -0
  18. package/dist/account/exchanges/binance/types.d.ts +3 -76
  19. package/dist/account/exchanges/binance/types.js +6 -0
  20. package/dist/account/exchanges/binance/ws.d.ts +3 -2
  21. package/dist/account/exchanges/binance/ws.js +6 -22
  22. package/dist/account/exchanges/bybit/asset-tracker.d.ts +25 -0
  23. package/dist/account/exchanges/bybit/asset-tracker.js +37 -0
  24. package/dist/account/exchanges/bybit/mapper.d.ts +4 -3
  25. package/dist/account/exchanges/bybit/mapper.js +5 -3
  26. package/dist/account/exchanges/bybit/orchestrator.d.ts +11 -12
  27. package/dist/account/exchanges/bybit/orchestrator.js +48 -134
  28. package/dist/account/exchanges/bybit/rest.d.ts +2 -3
  29. package/dist/account/exchanges/bybit/rest.js +4 -39
  30. package/dist/account/exchanges/bybit/state-handler.d.ts +22 -0
  31. package/dist/account/exchanges/bybit/state-handler.js +70 -0
  32. package/dist/account/exchanges/bybit/types.d.ts +14 -3
  33. package/dist/account/exchanges/bybit/types.js +7 -0
  34. package/dist/account/exchanges/bybit/ws.d.ts +1 -0
  35. package/dist/account/exchanges/bybit/ws.js +21 -12
  36. package/dist/account/exchanges/hyperliquid/asset-tracker.d.ts +34 -0
  37. package/dist/account/exchanges/hyperliquid/asset-tracker.js +83 -0
  38. package/dist/account/exchanges/hyperliquid/mapper.d.ts +4 -4
  39. package/dist/account/exchanges/hyperliquid/mapper.js +1 -0
  40. package/dist/account/exchanges/hyperliquid/orchestrator.d.ts +15 -28
  41. package/dist/account/exchanges/hyperliquid/orchestrator.js +79 -216
  42. package/dist/account/exchanges/hyperliquid/state-handler.d.ts +24 -0
  43. package/dist/account/exchanges/hyperliquid/state-handler.js +104 -0
  44. package/dist/account/exchanges/hyperliquid/types.d.ts +119 -5
  45. package/dist/account/exchanges/hyperliquid/types.js +9 -1
  46. package/dist/account/exchanges/index.d.ts +7 -5
  47. package/dist/account/exchanges/index.js +12 -4
  48. package/dist/account/index.d.ts +3 -1
  49. package/dist/account/types.d.ts +4 -73
  50. package/dist/account/types.js +17 -0
  51. package/dist/account/utils.d.ts +3 -1
  52. package/dist/index.d.ts +3 -1
  53. package/dist/types-D2dbAtin.d.ts +150 -0
  54. package/dist/utils/hmac.d.ts +1 -2
  55. package/dist/utils/hmac.js +1 -8
  56. package/dist/utils/ws.d.ts +6 -3
  57. package/dist/utils/ws.js +24 -7
  58. package/package.json +5 -5
  59. package/dist/account/exchanges/hyperliquid/rest.d.ts +0 -6
  60. package/dist/account/exchanges/hyperliquid/rest.js +0 -11
package/README.md CHANGED
@@ -1,45 +1,40 @@
1
- # @backend/exchanges-sdk
1
+ # @pear-protocol/exchanges-sdk
2
2
 
3
- Unified SDK for connecting to cryptocurrency derivative exchanges. Provides real-time account state tracking including balances, positions, and leverage management through a single, exchange-agnostic interface.
3
+ Unified SDK for connecting to cryptocurrency derivative exchanges. Provides real-time account state tracking including balances, positions, and leverage management through a single, exchange-agnostic interface with automatic WebSocket streaming and credential resolution.
4
4
 
5
5
  ## Supported Exchanges
6
6
 
7
7
  | Exchange | Connector Name | Market Type |
8
8
  |----------|---------------|-------------|
9
- | Binance | `binanceusdm` | USDM Futures |
9
+ | Binance | `binance` | USDM Futures |
10
10
  | Bybit | `bybit` | Linear Perpetuals |
11
11
  | Hyperliquid | `hyperliquid` | Perpetuals |
12
12
 
13
13
  ## Installation
14
14
 
15
- ```json
16
- {
17
- "dependencies": {
18
- "@backend/exchanges-sdk": "workspace:*"
19
- }
20
- }
15
+ ```bash
16
+ npm install @pear-protocol/exchanges-sdk
21
17
  ```
22
18
 
23
- Requires `@pear-protocol/core-sdk` and `@backend/shared` as peer dependencies.
24
-
25
19
  ## Quick Start
26
20
 
27
21
  ```typescript
28
22
  import PearSDK from '@pear-protocol/core-sdk';
29
- import { ExchangesSDK } from '@backend/exchanges-sdk';
23
+ import { ExchangesSDK } from '@pear-protocol/exchanges-sdk';
30
24
 
31
25
  const sdk = new PearSDK({ /* ... */ });
32
26
  const exchanges = new ExchangesSDK({ sdk });
33
27
 
34
28
  await exchanges.account.connect(tradeAccountId, 'binanceusdm');
35
29
 
30
+ // Track balance and positions
36
31
  const balanceTracker = exchanges.account.trackBalance((balance) => {
37
- console.log(balance.totalEquity);
32
+ console.log(balance.totalEquity, balance.unrealizedPnl);
38
33
  });
39
34
 
40
35
  const positionsTracker = exchanges.account.trackPositions((positions) => {
41
36
  for (const pos of positions) {
42
- console.log(pos.symbol, pos.side, pos.size);
37
+ console.log(pos.symbol, pos.side, pos.size, pos.entryPrice);
43
38
  }
44
39
  });
45
40
 
@@ -61,12 +56,20 @@ const exchanges = new ExchangesSDK({ sdk });
61
56
  const exchanges = new ExchangesSDK({ sdk, demo: true });
62
57
  ```
63
58
 
59
+ The `ExchangesSDK` exposes three top-level modules:
60
+
61
+ | Module | Description |
62
+ |--------|-------------|
63
+ | `exchanges.account` | Account connection, balance/position tracking, leverage management |
64
+ | `exchanges.credentials` | Exchange API credential fetching and caching |
65
+ | `exchanges.sdk` | Reference to the underlying `PearSDK` instance |
66
+
64
67
  ### Connecting to an Exchange
65
68
 
66
- Credentials are fetched automatically from the backend when you call `connect`.
69
+ Credentials are fetched automatically from the backend when you call `connect`. One exchange connection is active at a time.
67
70
 
68
71
  ```typescript
69
- await exchanges.account.connect(tradeAccountId, 'binanceusdm');
72
+ await exchanges.account.connect(tradeAccountId, 'binance');
70
73
  await exchanges.account.connect(tradeAccountId, 'bybit');
71
74
  await exchanges.account.connect(tradeAccountId, 'hyperliquid');
72
75
  ```
@@ -75,12 +78,12 @@ Check connection status:
75
78
 
76
79
  ```typescript
77
80
  exchanges.account.isConnected; // WebSocket is active
78
- exchanges.account.isInitialized; // First state snapshot received
81
+ exchanges.account.isInitialized; // first state snapshot received
79
82
  ```
80
83
 
81
84
  ### Tracking Balance
82
85
 
83
- Subscribe to real-time balance updates. The callback fires on every balance change.
86
+ Subscribe to real-time balance updates. The callback fires on every balance change from the exchange.
84
87
 
85
88
  ```typescript
86
89
  const tracker = exchanges.account.trackBalance((balance) => {
@@ -88,6 +91,8 @@ const tracker = exchanges.account.trackBalance((balance) => {
88
91
  balance.walletBalance; // wallet balance excluding unrealized PnL
89
92
  balance.unrealizedPnl; // total unrealized PnL
90
93
  balance.availableToTrade; // available margin for new trades
94
+ balance.initialMarginUsed; // margin used by open positions
95
+ balance.maintenanceMargin; // maintenance margin requirement
91
96
  balance.marginRatio; // current margin ratio
92
97
 
93
98
  // Per-asset breakdown
@@ -115,13 +120,13 @@ Subscribe to real-time position updates. The callback fires with the full list o
115
120
  ```typescript
116
121
  const tracker = exchanges.account.trackPositions((positions) => {
117
122
  for (const pos of positions) {
118
- pos.symbol; // "BTCUSDT"
119
- pos.side; // "long" | "short" | "both"
120
- pos.size; // "0.5"
121
- pos.entryPrice; // "65000.00"
122
- pos.unrealizedPnl; // "120.50"
123
- pos.leverage; // "10"
124
- pos.marginType; // "cross" | "isolated"
123
+ pos.symbol; // "BTCUSDT"
124
+ pos.side; // "long" | "short" | "both"
125
+ pos.size; // "0.5"
126
+ pos.entryPrice; // "65000.00"
127
+ pos.unrealizedPnl; // "120.50"
128
+ pos.leverage; // "10"
129
+ pos.marginType; // "cross" | "isolated"
125
130
  pos.liquidationPrice; // "58000.00" or null
126
131
  }
127
132
  });
@@ -130,15 +135,17 @@ const current = tracker.get();
130
135
  tracker.untrack();
131
136
  ```
132
137
 
138
+ Positions with a size of `"0"` are automatically removed from the tracked state.
139
+
133
140
  ### Tracking a Specific Asset
134
141
 
135
142
  Retrieve leverage, margin type, and trade size constraints for a specific coin. Useful when preparing to place a trade.
136
143
 
137
144
  ```typescript
138
145
  const tracker = exchanges.account.trackAsset('ETH', (info) => {
139
- info.coin; // "ETH"
140
- info.leverage; // "20"
141
- info.marginType; // "cross" | "isolated"
146
+ info.coin; // "ETH"
147
+ info.leverage; // "20"
148
+ info.marginType; // "cross" | "isolated"
142
149
  });
143
150
 
144
151
  const current = tracker.get();
@@ -148,8 +155,6 @@ tracker.untrack();
148
155
  ### Managing Leverage
149
156
 
150
157
  ```typescript
151
- await exchanges.account.setLeverage('BTCUSDT', '10');
152
-
153
158
  // Read cached leverage (returns null if not yet fetched)
154
159
  const leverage = exchanges.account.getLeverage('BTCUSDT');
155
160
  ```
@@ -161,16 +166,35 @@ Access a read-only snapshot of the complete account state at any time.
161
166
  ```typescript
162
167
  const state = exchanges.account.getState();
163
168
  if (state) {
164
- state.balance; // latest balance or null
165
- state.positions; // Map of all open positions
166
- state.leverageSettings; // Map of symbol to leverage
167
- state.trackedAssets; // Map of coin to asset info
169
+ state.balance; // latest AccountBalance or null
170
+ state.positions; // Map<"SYMBOL:side", AccountPosition>
171
+ state.leverageSettings; // Map<symbol, leverage>
172
+ state.trackedAssets; // Map<coin, TrackedAssetInfo>
168
173
  state.lastUpdated; // timestamp of last update
169
174
  state.initialized; // true after first snapshot
170
175
  }
171
176
  ```
172
177
 
173
- ### Disconnecting
178
+ ### Credential Management
179
+
180
+ Credentials are resolved automatically on `connect`, but you can manage them directly:
181
+
182
+ ```typescript
183
+ // Fetch credentials from the backend
184
+ const creds = await exchanges.credentials.fetch(tradeAccountId);
185
+
186
+ // Get cached credentials (null if not fetched)
187
+ const cached = exchanges.credentials.get(tradeAccountId);
188
+
189
+ // Get from cache or fetch if missing
190
+ const creds = await exchanges.credentials.getOrFetch(tradeAccountId);
191
+
192
+ // Clear cached credentials
193
+ exchanges.credentials.clear(tradeAccountId); // specific account
194
+ exchanges.credentials.clear(); // all accounts
195
+ ```
196
+
197
+ ### Cleanup
174
198
 
175
199
  ```typescript
176
200
  // Disconnect but keep credentials cached for reconnection
@@ -180,10 +204,3 @@ exchanges.account.disconnect();
180
204
  exchanges.destroy();
181
205
  ```
182
206
 
183
- ## Behavior Notes
184
-
185
- - The WebSocket connection starts lazily when the first tracker is registered and stops when all trackers are removed.
186
- - On unexpected disconnection, the SDK reconnects automatically with exponential backoff (1s to 30s).
187
- - Reconnection is suppressed after explicit `disconnect()` or `destroy()` calls.
188
- - Credentials are held in memory only and never persisted to disk.
189
- - All numeric values (balances, sizes, prices, PnL) are returned as strings to preserve decimal precision.
@@ -1,15 +1,15 @@
1
1
  import PearSDK from '@pear-protocol/core-sdk';
2
2
  import { Connector } from '@pear-protocol/types';
3
3
  import { Credentials } from '../credentials.js';
4
- import { BalanceCallback, Tracker, AccountBalance, PositionsCallback, AccountPosition, TrackedAssetCallback, TrackedAssetInfo, ExchangeState } from './types.js';
4
+ import { B as BalanceCallback, i as Tracker, a as AccountBalance, P as PositionsCallback, c as AccountPosition, T as TrackedAssetCallback, h as TrackedAssetInfo } from '../types-D2dbAtin.js';
5
+ import './exchanges/bybit/types.js';
6
+ import './exchanges/hyperliquid/types.js';
5
7
 
6
8
  declare class AccountManager {
7
9
  private credentials;
8
10
  private sdk;
9
11
  private demo;
10
12
  private activeAccount;
11
- private pendingExchange;
12
- private pendingTradeAccountId;
13
13
  private balanceListeners;
14
14
  private positionListeners;
15
15
  private assetListeners;
@@ -18,17 +18,11 @@ declare class AccountManager {
18
18
  trackBalance(cb: BalanceCallback): Tracker<AccountBalance | null>;
19
19
  trackPositions(cb: PositionsCallback): Tracker<AccountPosition[]>;
20
20
  trackAsset(coin: string, cb: TrackedAssetCallback): Tracker<TrackedAssetInfo | null>;
21
- setLeverage(symbol: string, leverage: string): Promise<void>;
22
21
  getLeverage(symbol: string): string | null;
23
- getState(): Readonly<ExchangeState> | null;
24
- disconnect(): void;
25
- destroy(): void;
22
+ disconnect(): Promise<void>;
23
+ destroy(): Promise<void>;
26
24
  get isConnected(): boolean;
27
25
  get isInitialized(): boolean;
28
- private get hasListeners();
29
- private disconnectWs;
30
- private maybeDisconnectWs;
31
- private ensureWsStarted;
32
26
  private resolveCredentials;
33
27
  private startWs;
34
28
  }
@@ -5,8 +5,6 @@ class AccountManager {
5
5
  sdk;
6
6
  demo;
7
7
  activeAccount = null;
8
- pendingExchange = null;
9
- pendingTradeAccountId = null;
10
8
  balanceListeners = /* @__PURE__ */ new Map();
11
9
  positionListeners = /* @__PURE__ */ new Map();
12
10
  assetListeners = /* @__PURE__ */ new Map();
@@ -16,28 +14,21 @@ class AccountManager {
16
14
  this.demo = demo;
17
15
  }
18
16
  async connect(tradeAccountId, exchange) {
17
+ await this.disconnect();
19
18
  const creds = await this.resolveCredentials(tradeAccountId, exchange);
20
- this.disconnectWs();
21
- this.pendingTradeAccountId = tradeAccountId;
22
- this.pendingExchange = exchange;
23
- if (this.hasListeners) {
24
- await this.startWs(exchange, creds);
25
- }
19
+ await this.startWs(exchange, creds);
26
20
  }
27
21
  trackBalance(cb) {
28
22
  const id = Math.random().toString(36).slice(2);
29
23
  this.balanceListeners.set(id, cb);
30
24
  if (this.activeAccount) {
31
- this.activeAccount.trackBalance(cb);
32
- } else {
33
- this.ensureWsStarted();
25
+ this.activeAccount.trackBalance(cb, id);
34
26
  }
35
27
  return {
36
28
  get: () => this.activeAccount?.getBalance() ?? null,
37
29
  untrack: () => {
38
30
  this.balanceListeners.delete(id);
39
31
  this.activeAccount?.untrackBalance(id);
40
- this.maybeDisconnectWs();
41
32
  }
42
33
  };
43
34
  }
@@ -45,16 +36,13 @@ class AccountManager {
45
36
  const id = Math.random().toString(36).slice(2);
46
37
  this.positionListeners.set(id, cb);
47
38
  if (this.activeAccount) {
48
- this.activeAccount.trackPositions(cb);
49
- } else {
50
- this.ensureWsStarted();
39
+ this.activeAccount.trackPositions(cb, id);
51
40
  }
52
41
  return {
53
42
  get: () => this.activeAccount?.getPositions() ?? [],
54
43
  untrack: () => {
55
44
  this.positionListeners.delete(id);
56
45
  this.activeAccount?.untrackPositions(id);
57
- this.maybeDisconnectWs();
58
46
  }
59
47
  };
60
48
  }
@@ -62,39 +50,30 @@ class AccountManager {
62
50
  const id = Math.random().toString(36).slice(2);
63
51
  this.assetListeners.set(id, { coin, cb });
64
52
  if (this.activeAccount) {
65
- this.activeAccount.trackAsset(coin, cb);
66
- } else {
67
- this.ensureWsStarted();
53
+ this.activeAccount.trackAsset(coin, cb, id);
68
54
  }
69
55
  return {
70
56
  get: () => this.activeAccount?.getTrackedAsset(coin) ?? null,
71
57
  untrack: () => {
72
58
  this.assetListeners.delete(id);
73
59
  this.activeAccount?.untrackAsset(id);
74
- this.maybeDisconnectWs();
75
60
  }
76
61
  };
77
62
  }
78
- async setLeverage(symbol, leverage) {
79
- if (!this.activeAccount) throw new Error("No active account");
80
- await this.activeAccount.setLeverage(symbol, leverage);
81
- }
82
63
  getLeverage(symbol) {
83
64
  return this.activeAccount?.getLeverage(symbol) ?? null;
84
65
  }
85
- getState() {
86
- return this.activeAccount?.getState() ?? null;
87
- }
88
- disconnect() {
89
- this.disconnectWs();
90
- this.pendingExchange = null;
91
- this.pendingTradeAccountId = null;
66
+ async disconnect() {
67
+ if (this.activeAccount) {
68
+ await this.activeAccount.disconnect();
69
+ this.activeAccount = null;
70
+ }
92
71
  this.balanceListeners.clear();
93
72
  this.positionListeners.clear();
94
73
  this.assetListeners.clear();
95
74
  }
96
- destroy() {
97
- this.disconnect();
75
+ async destroy() {
76
+ await this.disconnect();
98
77
  this.credentials.clear();
99
78
  }
100
79
  get isConnected() {
@@ -103,26 +82,6 @@ class AccountManager {
103
82
  get isInitialized() {
104
83
  return this.activeAccount?.isInitialized ?? false;
105
84
  }
106
- get hasListeners() {
107
- return this.balanceListeners.size > 0 || this.positionListeners.size > 0 || this.assetListeners.size > 0;
108
- }
109
- disconnectWs() {
110
- if (this.activeAccount) {
111
- this.activeAccount.destroy();
112
- this.activeAccount = null;
113
- }
114
- }
115
- maybeDisconnectWs() {
116
- if (!this.hasListeners) {
117
- this.disconnectWs();
118
- }
119
- }
120
- ensureWsStarted() {
121
- if (this.activeAccount || !this.pendingExchange || !this.pendingTradeAccountId) return;
122
- const exchange = this.pendingExchange;
123
- const tradeAccountId = this.pendingTradeAccountId;
124
- this.resolveCredentials(tradeAccountId, exchange).then((creds) => this.startWs(exchange, creds)).catch((err) => console.warn("[AccountManager] Failed to start WS:", err));
125
- }
126
85
  async resolveCredentials(tradeAccountId, exchange) {
127
86
  const creds = await this.credentials.getOrFetch(tradeAccountId);
128
87
  if (exchange === "hyperliquid") {
@@ -134,7 +93,10 @@ class AccountManager {
134
93
  if (!account.exchangeIdentifier) {
135
94
  throw new Error(`Trade account ${tradeAccountId} has no Hyperliquid address`);
136
95
  }
137
- return { ...creds, address: account.exchangeIdentifier };
96
+ return {
97
+ ...creds,
98
+ address: account.exchangeIdentifier
99
+ };
138
100
  }
139
101
  return creds;
140
102
  }
@@ -142,14 +104,14 @@ class AccountManager {
142
104
  const account = new ExchangeAccount({ exchange, demo: this.demo });
143
105
  await account.connect(creds);
144
106
  this.activeAccount = account;
145
- for (const cb of this.balanceListeners.values()) {
146
- account.trackBalance(cb);
107
+ for (const [id, cb] of this.balanceListeners.entries()) {
108
+ account.trackBalance(cb, id);
147
109
  }
148
- for (const cb of this.positionListeners.values()) {
149
- account.trackPositions(cb);
110
+ for (const [id, cb] of this.positionListeners.entries()) {
111
+ account.trackPositions(cb, id);
150
112
  }
151
- for (const { coin, cb } of this.assetListeners.values()) {
152
- account.trackAsset(coin, cb);
113
+ for (const [id, { coin, cb }] of this.assetListeners.entries()) {
114
+ account.trackAsset(coin, cb, id);
153
115
  }
154
116
  }
155
117
  }
@@ -1,5 +1,7 @@
1
- import { Connector } from '@pear-protocol/types';
2
- import { AccountConfig, SupportedCredentials, BalanceCallback, AccountBalance, PositionsCallback, AccountPosition, TrackedAssetCallback, TrackedAssetInfo, ExchangeState } from './types.js';
1
+ import { b as AccountConfig, g as SupportedCredentials, B as BalanceCallback, a as AccountBalance, P as PositionsCallback, c as AccountPosition, T as TrackedAssetCallback, h as TrackedAssetInfo } from '../types-D2dbAtin.js';
2
+ import '@pear-protocol/types';
3
+ import './exchanges/bybit/types.js';
4
+ import './exchanges/hyperliquid/types.js';
3
5
 
4
6
  declare class ExchangeAccount {
5
7
  private exchange;
@@ -12,24 +14,20 @@ declare class ExchangeAccount {
12
14
  private onAuthError?;
13
15
  constructor(config: AccountConfig);
14
16
  connect(credentials: SupportedCredentials): Promise<void>;
15
- disconnect(): void;
16
- destroy(): void;
17
+ disconnect(): Promise<void>;
17
18
  get isConnected(): boolean;
18
19
  get isInitialized(): boolean;
19
20
  get hasListeners(): boolean;
20
- trackBalance(cb: BalanceCallback): string;
21
+ trackBalance(cb: BalanceCallback, id?: string): string;
21
22
  untrackBalance(id: string): void;
22
23
  getBalance(): AccountBalance | null;
23
- trackPositions(cb: PositionsCallback): string;
24
+ trackPositions(cb: PositionsCallback, id?: string): string;
24
25
  untrackPositions(id: string): void;
25
26
  getPositions(): AccountPosition[];
26
- trackAsset(coin: string, cb: TrackedAssetCallback): string;
27
+ trackAsset(coin: string, cb: TrackedAssetCallback, id: string): string;
27
28
  untrackAsset(id: string): void;
28
29
  getTrackedAsset(coin: string): TrackedAssetInfo | null;
29
30
  getLeverage(symbol: string): string | null;
30
- setLeverage(symbol: string, leverage: string): Promise<void>;
31
- getState(): Readonly<ExchangeState>;
32
- getExchange(): Connector;
33
31
  private applyStateUpdate;
34
32
  }
35
33
 
@@ -17,32 +17,32 @@ class ExchangeAccount {
17
17
  this.state = createEmptyState();
18
18
  }
19
19
  async connect(credentials) {
20
- this.disconnect();
20
+ await this.disconnect();
21
21
  this.state = createEmptyState();
22
- this.orchestrator = createOrchestrator(this.exchange, {
23
- onError: (error) => {
24
- console.warn(`[ExchangeAccount] ${this.exchange} error:`, error.message);
25
- if (error.message.includes("auth") || error.message.includes("login")) {
26
- this.onAuthError?.();
27
- }
22
+ this.orchestrator = createOrchestrator(
23
+ this.exchange,
24
+ {
25
+ onError: (error) => {
26
+ console.warn(`[ExchangeAccount] ${this.exchange} error:`, error.message);
27
+ if (error.message.includes("auth") || error.message.includes("login")) {
28
+ this.onAuthError?.();
29
+ }
30
+ },
31
+ onStateUpdate: (update) => this.applyStateUpdate(update),
32
+ demo: this.demo
28
33
  },
29
- onStateUpdate: (update) => this.applyStateUpdate(update),
30
- demo: this.demo
31
- });
32
- await this.orchestrator.start(credentials);
33
- }
34
- disconnect() {
35
- if (this.orchestrator) {
36
- this.orchestrator.stop();
37
- this.orchestrator = null;
38
- }
34
+ credentials
35
+ );
36
+ await this.orchestrator.start();
39
37
  }
40
- destroy() {
41
- this.disconnect();
38
+ async disconnect() {
42
39
  this.balanceListeners.clear();
43
40
  this.positionListeners.clear();
44
41
  this.assetListeners.clear();
45
- this.state = createEmptyState();
42
+ if (this.orchestrator) {
43
+ await this.orchestrator.stop();
44
+ this.orchestrator = null;
45
+ }
46
46
  }
47
47
  get isConnected() {
48
48
  return this.orchestrator?.isConnected ?? false;
@@ -53,14 +53,11 @@ class ExchangeAccount {
53
53
  get hasListeners() {
54
54
  return this.balanceListeners.size > 0 || this.positionListeners.size > 0 || this.assetListeners.size > 0;
55
55
  }
56
- trackBalance(cb) {
57
- const id = Math.random().toString(36).slice(2);
56
+ trackBalance(cb, id) {
57
+ id ??= Math.random().toString(36).slice(2);
58
58
  this.balanceListeners.set(id, cb);
59
59
  if (this.state.balance) {
60
- try {
61
- cb(this.state.balance);
62
- } catch {
63
- }
60
+ cb(this.state.balance);
64
61
  }
65
62
  return id;
66
63
  }
@@ -70,14 +67,11 @@ class ExchangeAccount {
70
67
  getBalance() {
71
68
  return this.state.balance;
72
69
  }
73
- trackPositions(cb) {
74
- const id = Math.random().toString(36).slice(2);
70
+ trackPositions(cb, id) {
71
+ id ??= Math.random().toString(36).slice(2);
75
72
  this.positionListeners.set(id, cb);
76
73
  if (this.state.initialized) {
77
- try {
78
- cb(Array.from(this.state.positions.values()));
79
- } catch {
80
- }
74
+ cb(Array.from(this.state.positions.values()));
81
75
  }
82
76
  return id;
83
77
  }
@@ -87,16 +81,12 @@ class ExchangeAccount {
87
81
  getPositions() {
88
82
  return Array.from(this.state.positions.values());
89
83
  }
90
- trackAsset(coin, cb) {
91
- const id = Math.random().toString(36).slice(2);
84
+ trackAsset(coin, cb, id) {
92
85
  this.assetListeners.set(id, { coin, cb });
93
86
  this.orchestrator?.trackAsset(coin);
94
87
  const existing = this.state.trackedAssets.get(coin);
95
88
  if (existing) {
96
- try {
97
- cb(existing);
98
- } catch {
99
- }
89
+ cb(existing);
100
90
  }
101
91
  return id;
102
92
  }
@@ -120,17 +110,6 @@ class ExchangeAccount {
120
110
  }
121
111
  return null;
122
112
  }
123
- async setLeverage(symbol, leverage) {
124
- if (!this.orchestrator) throw new Error("Not connected");
125
- await this.orchestrator.setLeverage(symbol, leverage);
126
- }
127
- getState() {
128
- return this.state;
129
- }
130
- getExchange() {
131
- return this.exchange;
132
- }
133
- // --- private ---
134
113
  applyStateUpdate(update) {
135
114
  if (update.balance) {
136
115
  this.state.balance = update.balance;
@@ -151,10 +130,7 @@ class ExchangeAccount {
151
130
  }
152
131
  const positions = Array.from(this.state.positions.values());
153
132
  for (const cb of this.positionListeners.values()) {
154
- try {
155
- cb(positions);
156
- } catch {
157
- }
133
+ cb(positions);
158
134
  }
159
135
  }
160
136
  if (update.leverageSettings) {
@@ -167,10 +143,7 @@ class ExchangeAccount {
167
143
  this.state.trackedAssets.set(coin, info);
168
144
  for (const listener of this.assetListeners.values()) {
169
145
  if (listener.coin === coin) {
170
- try {
171
- listener.cb(info);
172
- } catch {
173
- }
146
+ listener.cb(info);
174
147
  }
175
148
  }
176
149
  }
@@ -1,5 +1,7 @@
1
- import { SupportedCredentials, StateUpdate } from '../types.js';
1
+ import { S as StateUpdate, g as SupportedCredentials } from '../../types-D2dbAtin.js';
2
2
  import '@pear-protocol/types';
3
+ import './bybit/types.js';
4
+ import './hyperliquid/types.js';
3
5
 
4
6
  type StateUpdateHandler = (update: StateUpdate) => void;
5
7
  type ErrorHandler = (error: Error) => void;
@@ -9,41 +11,37 @@ type OrchestratorConfig = {
9
11
  demo: boolean;
10
12
  };
11
13
  interface Orchestrator {
12
- start: (credentials: SupportedCredentials) => Promise<void>;
13
- stop: () => void;
14
+ stop: () => Promise<void>;
14
15
  isConnected: boolean;
15
16
  onAssetTracked: (coin: string) => void;
16
17
  onAssetUntracked: (coin: string) => void;
17
- setLeverage: (symbol: string, leverage: string) => Promise<void>;
18
18
  trackAsset: (coin: string) => void;
19
19
  untrackAsset: (coin: string) => boolean;
20
20
  getTrackedCoins: () => ReadonlySet<string>;
21
+ start: () => Promise<void>;
21
22
  }
22
23
  declare abstract class BaseOrchestrator implements Orchestrator {
23
24
  protected onStateUpdate: StateUpdateHandler;
24
25
  protected onError: ErrorHandler;
25
26
  protected demo: boolean;
26
27
  protected credentials: SupportedCredentials | null;
27
- protected manualClose: boolean;
28
- protected reconnectAttempts: number;
29
- protected reconnectTimer: ReturnType<typeof setTimeout> | null;
28
+ protected stopped: boolean;
30
29
  protected trackedCoins: Set<string>;
31
- protected startGeneration: number;
32
- private starting;
33
30
  constructor(config: OrchestratorConfig);
34
- abstract start(credentials: SupportedCredentials): Promise<void>;
35
- abstract stop(): void;
31
+ /** Awaitable first-time connection. Errors propagate to caller. */
32
+ start(): Promise<void>;
33
+ abstract stop(): Promise<void>;
36
34
  abstract get isConnected(): boolean;
37
35
  abstract onAssetTracked(coin: string): void;
38
36
  abstract onAssetUntracked(coin: string): void;
39
- abstract setLeverage(symbol: string, leverage: string): Promise<void>;
37
+ /** Subclass implements the actual async init (REST fetches, WS connect, etc.) */
38
+ protected abstract initAsync(): Promise<void>;
39
+ /** Subclass cleans up connections on stop. */
40
+ protected abstract cleanupConnections(): void | Promise<void>;
40
41
  trackAsset(coin: string): void;
41
42
  untrackAsset(coin: string): boolean;
42
43
  getTrackedCoins(): ReadonlySet<string>;
43
44
  protected canUntrackAsset(_coin: string): boolean;
44
- protected scheduleReconnect(): void;
45
- protected clearReconnectTimer(): void;
46
- private guardedStart;
47
45
  }
48
46
 
49
47
  export { BaseOrchestrator, type Orchestrator, type OrchestratorConfig };