@pear-protocol/exchanges-sdk 0.0.18 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -137
- package/dist/client/base-ws.d.ts +30 -0
- package/dist/client/base-ws.js +113 -0
- package/dist/client/binance/exchange-ws.d.ts +19 -0
- package/dist/client/binance/exchange-ws.js +26 -0
- package/dist/{account/exchanges → client}/binance/rest.d.ts +2 -6
- package/dist/{account/exchanges → client}/binance/rest.js +4 -11
- package/dist/client/binance/types.d.ts +68 -0
- package/dist/client/binance/types.js +1 -0
- package/dist/client/binance/ws.d.ts +30 -0
- package/dist/client/binance/ws.js +54 -0
- package/dist/client/bybit/exchange-ws.d.ts +20 -0
- package/dist/client/bybit/exchange-ws.js +28 -0
- package/dist/{account/exchanges → client}/bybit/rest.js +3 -2
- package/dist/{account/exchanges → client}/bybit/types.d.ts +1 -9
- package/dist/client/bybit/types.js +1 -0
- package/dist/client/bybit/ws.d.ts +17 -0
- package/dist/client/bybit/ws.js +50 -0
- package/dist/client/exchange-ws.d.ts +12 -0
- package/dist/client/exchange-ws.js +10 -0
- package/dist/client/hyperliquid/exchange-ws.d.ts +15 -0
- package/dist/client/hyperliquid/exchange-ws.js +21 -0
- package/dist/client/hyperliquid/rest.d.ts +3 -0
- package/dist/client/hyperliquid/rest.js +12 -0
- package/dist/{account/exchanges → client}/hyperliquid/types.d.ts +18 -39
- package/dist/client/hyperliquid/types.js +1 -0
- package/dist/client/hyperliquid/ws.d.ts +10 -0
- package/dist/client/hyperliquid/ws.js +16 -0
- package/dist/client/index.d.ts +31 -0
- package/dist/client/index.js +47 -0
- package/dist/client/lighter/constants.d.ts +4 -0
- package/dist/client/lighter/constants.js +4 -0
- package/dist/client/lighter/exchange-ws.d.ts +15 -0
- package/dist/client/lighter/exchange-ws.js +21 -0
- package/dist/client/lighter/rest.d.ts +56 -0
- package/dist/client/lighter/rest.js +27 -0
- package/dist/client/lighter/types.d.ts +32 -0
- package/dist/client/lighter/types.js +1 -0
- package/dist/client/lighter/ws.d.ts +10 -0
- package/dist/client/lighter/ws.js +17 -0
- package/dist/client/okx/exchange-ws.d.ts +20 -0
- package/dist/client/okx/exchange-ws.js +28 -0
- package/dist/{account/exchanges → client}/okx/rest.js +2 -2
- package/dist/{account/exchanges → client}/okx/types.d.ts +1 -10
- package/dist/client/okx/types.js +1 -0
- package/dist/client/okx/ws.d.ts +17 -0
- package/dist/client/okx/ws.js +61 -0
- package/dist/helper/factory.d.ts +10 -0
- package/dist/helper/factory.js +12 -0
- package/dist/helper/index.d.ts +7 -0
- package/dist/helper/index.js +2 -0
- package/dist/helper/lighter/ethereum/abi.d.ts +66 -0
- package/dist/helper/lighter/ethereum/abi.js +40 -0
- package/dist/helper/lighter/ethereum/index.d.ts +8 -0
- package/dist/helper/lighter/ethereum/index.js +34 -0
- package/dist/helper/lighter/ethereum/types.d.ts +23 -0
- package/dist/helper/lighter/ethereum/types.js +1 -0
- package/dist/helper/lighter/helper.d.ts +9 -0
- package/dist/helper/lighter/helper.js +67 -0
- package/dist/helper/lighter/index.d.ts +6 -0
- package/dist/helper/lighter/index.js +2 -0
- package/dist/helper/lighter/types.d.ts +63 -0
- package/dist/helper/lighter/types.js +35 -0
- package/dist/helper/types.d.ts +11 -0
- package/dist/helper/types.js +1 -0
- package/dist/index.d.ts +37 -24
- package/dist/index.js +6 -21
- package/dist/sdk.d.ts +43 -0
- package/dist/sdk.js +52 -0
- package/dist/syncer/base.d.ts +33 -0
- package/dist/syncer/base.js +14 -0
- package/dist/syncer/index.d.ts +18 -0
- package/dist/syncer/index.js +1 -0
- package/dist/tracker/base.d.ts +79 -0
- package/dist/tracker/base.js +120 -0
- package/dist/tracker/binance/const.d.ts +4 -0
- package/dist/tracker/binance/const.js +4 -0
- package/dist/{account/exchanges → tracker}/binance/mapper.d.ts +4 -1
- package/dist/{account/exchanges → tracker}/binance/mapper.js +7 -6
- package/dist/tracker/binance/tracker.d.ts +60 -0
- package/dist/tracker/binance/tracker.js +181 -0
- package/dist/tracker/binance/types.d.ts +6 -0
- package/dist/{account/exchanges → tracker}/bybit/mapper.d.ts +5 -3
- package/dist/{account/exchanges → tracker}/bybit/mapper.js +11 -10
- package/dist/tracker/bybit/tracker.d.ts +51 -0
- package/dist/tracker/bybit/tracker.js +138 -0
- package/dist/tracker/bybit/types.d.ts +7 -0
- package/dist/{account/exchanges → tracker}/hyperliquid/mapper.d.ts +6 -4
- package/dist/{account/exchanges → tracker}/hyperliquid/mapper.js +18 -16
- package/dist/tracker/hyperliquid/tracker.d.ts +62 -0
- package/dist/tracker/hyperliquid/tracker.js +219 -0
- package/dist/tracker/hyperliquid/types.d.ts +23 -0
- package/dist/tracker/index.d.ts +38 -0
- package/dist/tracker/index.js +81 -0
- package/dist/tracker/lighter/const.d.ts +3 -0
- package/dist/tracker/lighter/const.js +3 -0
- package/dist/tracker/lighter/mapper.d.ts +16 -0
- package/dist/tracker/lighter/mapper.js +77 -0
- package/dist/tracker/lighter/tracker.d.ts +53 -0
- package/dist/tracker/lighter/tracker.js +150 -0
- package/dist/tracker/lighter/types.d.ts +16 -0
- package/dist/tracker/lighter/types.js +6 -0
- package/dist/{account/exchanges → tracker}/okx/mapper.d.ts +5 -3
- package/dist/{account/exchanges → tracker}/okx/mapper.js +11 -10
- package/dist/tracker/okx/tracker.d.ts +51 -0
- package/dist/tracker/okx/tracker.js +134 -0
- package/dist/tracker/okx/types.d.ts +8 -0
- package/dist/tracker/types.d.ts +63 -0
- package/dist/{account → tracker}/types.js +6 -4
- package/dist/tracker/utils.d.ts +11 -0
- package/dist/{account → tracker}/utils.js +0 -2
- package/dist/types.d.ts +50 -0
- package/dist/types.js +1 -0
- package/package.json +8 -4
- package/dist/account/account-manager.d.ts +0 -31
- package/dist/account/account-manager.js +0 -119
- package/dist/account/exchange-account.d.ts +0 -35
- package/dist/account/exchange-account.js +0 -158
- package/dist/account/exchanges/base.d.ts +0 -48
- package/dist/account/exchanges/base.js +0 -41
- package/dist/account/exchanges/binance/asset-tracker.d.ts +0 -26
- package/dist/account/exchanges/binance/asset-tracker.js +0 -38
- package/dist/account/exchanges/binance/const.d.ts +0 -10
- package/dist/account/exchanges/binance/const.js +0 -10
- package/dist/account/exchanges/binance/orchestrator.d.ts +0 -31
- package/dist/account/exchanges/binance/orchestrator.js +0 -91
- package/dist/account/exchanges/binance/state-handler.d.ts +0 -33
- package/dist/account/exchanges/binance/state-handler.js +0 -113
- package/dist/account/exchanges/binance/types.d.ts +0 -5
- package/dist/account/exchanges/binance/ws.d.ts +0 -23
- package/dist/account/exchanges/binance/ws.js +0 -69
- package/dist/account/exchanges/bybit/asset-tracker.d.ts +0 -26
- package/dist/account/exchanges/bybit/asset-tracker.js +0 -37
- package/dist/account/exchanges/bybit/const.d.ts +0 -7
- package/dist/account/exchanges/bybit/const.js +0 -7
- package/dist/account/exchanges/bybit/orchestrator.d.ts +0 -22
- package/dist/account/exchanges/bybit/orchestrator.js +0 -66
- package/dist/account/exchanges/bybit/state-handler.d.ts +0 -23
- package/dist/account/exchanges/bybit/state-handler.js +0 -70
- package/dist/account/exchanges/bybit/ws.d.ts +0 -19
- package/dist/account/exchanges/bybit/ws.js +0 -83
- package/dist/account/exchanges/hyperliquid/asset-tracker.d.ts +0 -35
- package/dist/account/exchanges/hyperliquid/asset-tracker.js +0 -83
- package/dist/account/exchanges/hyperliquid/orchestrator.d.ts +0 -26
- package/dist/account/exchanges/hyperliquid/orchestrator.js +0 -102
- package/dist/account/exchanges/hyperliquid/state-handler.d.ts +0 -25
- package/dist/account/exchanges/hyperliquid/state-handler.js +0 -104
- package/dist/account/exchanges/index.d.ts +0 -11
- package/dist/account/exchanges/index.js +0 -32
- package/dist/account/exchanges/okx/asset-tracker.d.ts +0 -26
- package/dist/account/exchanges/okx/asset-tracker.js +0 -34
- package/dist/account/exchanges/okx/const.d.ts +0 -7
- package/dist/account/exchanges/okx/const.js +0 -7
- package/dist/account/exchanges/okx/orchestrator.d.ts +0 -22
- package/dist/account/exchanges/okx/orchestrator.js +0 -57
- package/dist/account/exchanges/okx/state-handler.d.ts +0 -24
- package/dist/account/exchanges/okx/state-handler.js +0 -78
- package/dist/account/exchanges/okx/ws.d.ts +0 -20
- package/dist/account/exchanges/okx/ws.js +0 -103
- package/dist/account/index.d.ts +0 -11
- package/dist/account/index.js +0 -4
- package/dist/account/types.d.ts +0 -5
- package/dist/account/utils.d.ts +0 -10
- package/dist/credentials.d.ts +0 -28
- package/dist/credentials.js +0 -46
- package/dist/types-Caz2X4h0.d.ts +0 -151
- package/dist/utils/ws.d.ts +0 -10
- package/dist/utils/ws.js +0 -42
- /package/dist/{account/exchanges → client}/bybit/rest.d.ts +0 -0
- /package/dist/{utils → client}/hmac.d.ts +0 -0
- /package/dist/{utils → client}/hmac.js +0 -0
- /package/dist/{account/exchanges → client}/okx/rest.d.ts +0 -0
- /package/dist/{account/exchanges → tracker}/binance/types.js +0 -0
- /package/dist/{account/exchanges → tracker}/bybit/types.js +0 -0
- /package/dist/{account/exchanges → tracker}/hyperliquid/const.d.ts +0 -0
- /package/dist/{account/exchanges → tracker}/hyperliquid/const.js +0 -0
- /package/dist/{account/exchanges → tracker}/hyperliquid/types.js +0 -0
- /package/dist/{account/exchanges → tracker}/okx/types.js +0 -0
package/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# @pear-protocol/exchanges-sdk
|
|
2
2
|
|
|
3
|
-
Unified SDK for
|
|
3
|
+
Unified SDK for real-time account state (balance, positions, per-asset leverage) across supported derivative exchanges.
|
|
4
4
|
|
|
5
5
|
## Supported Exchanges
|
|
6
6
|
|
|
7
|
-
| Exchange | Connector
|
|
8
|
-
|
|
7
|
+
| Exchange | Connector | Market |
|
|
8
|
+
|----------|-----------|--------|
|
|
9
9
|
| Binance | `binance` | USDM Futures |
|
|
10
10
|
| Bybit | `bybit` | Linear Perpetuals |
|
|
11
11
|
| Hyperliquid | `hyperliquid` | Perpetuals |
|
|
12
|
-
|
|
|
12
|
+
| Lighter | `lighter` | Perpetuals |
|
|
13
|
+
| OKX | `okx` | Linear SWAP |
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -26,187 +27,122 @@ import { ExchangesSDK } from '@pear-protocol/exchanges-sdk';
|
|
|
26
27
|
const sdk = new PearSDK({ /* ... */ });
|
|
27
28
|
const exchanges = new ExchangesSDK({ sdk });
|
|
28
29
|
|
|
29
|
-
await exchanges.
|
|
30
|
+
const connection = await exchanges.connect(tradeAccountId, 'binance');
|
|
31
|
+
const tracker = exchanges.createTracker(connection);
|
|
32
|
+
await tracker.start();
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
const balanceTracker = exchanges.account.trackBalance((balance) => {
|
|
34
|
+
const offBalance = tracker.trackBalance((balance) => {
|
|
33
35
|
console.log(balance.totalEquity, balance.unrealizedPnl);
|
|
34
36
|
});
|
|
35
37
|
|
|
36
|
-
const
|
|
37
|
-
for (const
|
|
38
|
-
console.log(pos.symbol, pos.side, pos.size, pos.entryPrice);
|
|
39
|
-
}
|
|
38
|
+
const offPositions = tracker.trackPosition((positions) => {
|
|
39
|
+
for (const p of positions) console.log(p.symbol, p.side, p.size);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
// Cleanup
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
offBalance();
|
|
44
|
+
offPositions();
|
|
45
|
+
await tracker.disconnect();
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
### Initialization
|
|
48
|
+
Pass `demo: true` to target testnet:
|
|
51
49
|
|
|
52
50
|
```typescript
|
|
53
|
-
// Production
|
|
54
|
-
const exchanges = new ExchangesSDK({ sdk });
|
|
55
|
-
|
|
56
|
-
// Testnet / demo
|
|
57
51
|
const exchanges = new ExchangesSDK({ sdk, demo: true });
|
|
58
52
|
```
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| Module | Description |
|
|
63
|
-
|--------|-------------|
|
|
64
|
-
| `exchanges.account` | Account connection, balance/position tracking, leverage management |
|
|
65
|
-
| `exchanges.credentials` | Exchange API credential fetching and caching |
|
|
66
|
-
| `exchanges.sdk` | Reference to the underlying `PearSDK` instance |
|
|
67
|
-
|
|
68
|
-
### Connecting to an Exchange
|
|
69
|
-
|
|
70
|
-
Credentials are fetched automatically from the backend when you call `connect`. One exchange connection is active at a time.
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
await exchanges.account.connect(tradeAccountId, 'binance');
|
|
74
|
-
await exchanges.account.connect(tradeAccountId, 'bybit');
|
|
75
|
-
await exchanges.account.connect(tradeAccountId, 'hyperliquid');
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Check connection status:
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
exchanges.account.isConnected; // WebSocket is active
|
|
82
|
-
exchanges.account.isInitialized; // first state snapshot received
|
|
83
|
-
```
|
|
54
|
+
## Tracking
|
|
84
55
|
|
|
85
|
-
|
|
56
|
+
Each `track*` method returns an unsubscribe function. Callbacks fire with the current snapshot on subscribe (if available) and on every update.
|
|
86
57
|
|
|
87
|
-
|
|
58
|
+
### Balance
|
|
88
59
|
|
|
89
60
|
```typescript
|
|
90
|
-
|
|
91
|
-
balance.totalEquity;
|
|
92
|
-
balance.walletBalance;
|
|
93
|
-
balance.unrealizedPnl;
|
|
94
|
-
balance.availableToTrade;
|
|
95
|
-
balance.initialMarginUsed;
|
|
96
|
-
balance.maintenanceMargin;
|
|
97
|
-
balance.marginRatio;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
for (const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
asset.usdValue; // USD equivalent
|
|
61
|
+
tracker.trackBalance((balance) => {
|
|
62
|
+
balance.totalEquity;
|
|
63
|
+
balance.walletBalance;
|
|
64
|
+
balance.unrealizedPnl;
|
|
65
|
+
balance.availableToTrade;
|
|
66
|
+
balance.initialMarginUsed;
|
|
67
|
+
balance.maintenanceMargin;
|
|
68
|
+
balance.marginRatio;
|
|
69
|
+
balance.accountType;
|
|
70
|
+
|
|
71
|
+
for (const a of balance.assets) {
|
|
72
|
+
a.asset; // "USDT", "USDC", ...
|
|
73
|
+
a.free;
|
|
74
|
+
a.used;
|
|
75
|
+
a.total;
|
|
76
|
+
a.usdValue;
|
|
107
77
|
}
|
|
108
78
|
});
|
|
109
|
-
|
|
110
|
-
// Read the latest value without waiting for a callback
|
|
111
|
-
const current = tracker.get();
|
|
112
|
-
|
|
113
|
-
// Stop receiving updates
|
|
114
|
-
tracker.untrack();
|
|
115
79
|
```
|
|
116
80
|
|
|
117
|
-
###
|
|
118
|
-
|
|
119
|
-
Subscribe to real-time position updates. The callback fires with the full list of open positions on every change.
|
|
81
|
+
### Positions
|
|
120
82
|
|
|
121
83
|
```typescript
|
|
122
|
-
|
|
123
|
-
for (const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
84
|
+
tracker.trackPosition((positions) => {
|
|
85
|
+
for (const p of positions) {
|
|
86
|
+
p.symbol;
|
|
87
|
+
p.side; // "long" | "short" | "both"
|
|
88
|
+
p.size;
|
|
89
|
+
p.entryPrice;
|
|
90
|
+
p.unrealizedPnl;
|
|
91
|
+
p.leverage;
|
|
92
|
+
p.marginType; // "cross" | "isolated"
|
|
93
|
+
p.liquidationPrice; // string | null
|
|
132
94
|
}
|
|
133
95
|
});
|
|
134
|
-
|
|
135
|
-
const current = tracker.get();
|
|
136
|
-
tracker.untrack();
|
|
137
96
|
```
|
|
138
97
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
### Tracking a Specific Asset
|
|
98
|
+
Closed positions (`size === '0'`) are dropped automatically.
|
|
142
99
|
|
|
143
|
-
|
|
100
|
+
### Per-Asset Leverage & Margin
|
|
144
101
|
|
|
145
102
|
```typescript
|
|
146
|
-
|
|
147
|
-
info.coin;
|
|
148
|
-
info.leverage;
|
|
149
|
-
info.marginType;
|
|
103
|
+
tracker.trackAsset('ETH', (info) => {
|
|
104
|
+
info.coin;
|
|
105
|
+
info.leverage;
|
|
106
|
+
info.marginType;
|
|
150
107
|
});
|
|
151
|
-
|
|
152
|
-
const current = tracker.get();
|
|
153
|
-
tracker.untrack();
|
|
154
108
|
```
|
|
155
109
|
|
|
156
|
-
|
|
157
|
-
> - Binance / Bybit: `BTCUSDT`
|
|
158
|
-
> - Hyperliquid: `BTC`
|
|
159
|
-
> - OKX: `BTC-USDT-SWAP`
|
|
110
|
+
Asset symbol format per exchange:
|
|
160
111
|
|
|
161
|
-
|
|
112
|
+
| Exchange | Format | Example |
|
|
113
|
+
|----------|--------|---------|
|
|
114
|
+
| Binance | `<BASE><QUOTE>` | `BTCUSDT` |
|
|
115
|
+
| Bybit | `<BASE><QUOTE>` | `BTCUSDT` |
|
|
116
|
+
| Hyperliquid | base coin | `BTC` |
|
|
117
|
+
| Lighter | base coin | `BTC` |
|
|
118
|
+
| OKX | `instId` | `BTC-USDT-SWAP` |
|
|
162
119
|
|
|
163
|
-
|
|
164
|
-
// Read cached leverage (returns null if not yet fetched)
|
|
165
|
-
const leverage = exchanges.account.getLeverage('BTCUSDT');
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Reading Full State
|
|
120
|
+
## Snapshot Reads
|
|
169
121
|
|
|
170
|
-
|
|
122
|
+
Synchronous getters for the latest cached state:
|
|
171
123
|
|
|
172
124
|
```typescript
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
state.trackedAssets; // Map<coin, TrackedAssetInfo>
|
|
179
|
-
state.lastUpdated; // timestamp of last update
|
|
180
|
-
state.initialized; // true after first snapshot
|
|
181
|
-
}
|
|
125
|
+
tracker.getBalance(); // AccountBalance | null
|
|
126
|
+
tracker.getPositions(); // AccountPosition[]
|
|
127
|
+
tracker.getTrackedAsset('ETH'); // TrackedAssetInfo | null
|
|
128
|
+
tracker.isConnected;
|
|
129
|
+
tracker.isInitialized; // true after first snapshot
|
|
182
130
|
```
|
|
183
131
|
|
|
184
|
-
|
|
132
|
+
## Account Type Labels
|
|
185
133
|
|
|
186
|
-
|
|
134
|
+
`balance.accountType` is an exchange-specific enum. Render with:
|
|
187
135
|
|
|
188
136
|
```typescript
|
|
189
|
-
|
|
190
|
-
const creds = await exchanges.credentials.fetch(tradeAccountId);
|
|
137
|
+
import { ACCOUNT_TYPE_LABELS } from '@pear-protocol/exchanges-sdk';
|
|
191
138
|
|
|
192
|
-
//
|
|
193
|
-
const cached = exchanges.credentials.get(tradeAccountId);
|
|
194
|
-
|
|
195
|
-
// Get from cache or fetch if missing
|
|
196
|
-
const creds = await exchanges.credentials.getOrFetch(tradeAccountId);
|
|
197
|
-
|
|
198
|
-
// Clear cached credentials
|
|
199
|
-
exchanges.credentials.clear(tradeAccountId); // specific account
|
|
200
|
-
exchanges.credentials.clear(); // all accounts
|
|
139
|
+
ACCOUNT_TYPE_LABELS[balance.accountType]; // "Cross Margin", "Portfolio Margin", ...
|
|
201
140
|
```
|
|
202
141
|
|
|
203
|
-
|
|
142
|
+
## Cleanup
|
|
204
143
|
|
|
205
144
|
```typescript
|
|
206
|
-
|
|
207
|
-
exchanges.account.disconnect();
|
|
208
|
-
|
|
209
|
-
// Full teardown: disconnect and clear all credentials
|
|
210
|
-
exchanges.destroy();
|
|
145
|
+
await tracker.disconnect();
|
|
211
146
|
```
|
|
212
147
|
|
|
148
|
+
Credentials are fetched automatically on `connect`; for Hyperliquid and Lighter the account index is resolved from the backend.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { WebSocket } from 'partysocket';
|
|
2
|
+
|
|
3
|
+
interface WsMessage {
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
type MessageListener = (data: string) => void;
|
|
7
|
+
type OpenListener = () => void;
|
|
8
|
+
declare abstract class BaseWs {
|
|
9
|
+
protected ws: WebSocket | null;
|
|
10
|
+
protected pingTimer: ReturnType<typeof setInterval> | null;
|
|
11
|
+
private messageListeners;
|
|
12
|
+
private openListeners;
|
|
13
|
+
private nextListenerId;
|
|
14
|
+
protected abstract getWsUrl(): string;
|
|
15
|
+
protected abstract getPingIntervalMs(): number;
|
|
16
|
+
protected abstract buildPingMessage(): WsMessage | string | null;
|
|
17
|
+
connect(): Promise<void>;
|
|
18
|
+
disconnect(): void;
|
|
19
|
+
get connected(): boolean;
|
|
20
|
+
send(msg: WsMessage | string): void;
|
|
21
|
+
addMessageListener(listener: MessageListener): string;
|
|
22
|
+
removeMessageListener(id: string): void;
|
|
23
|
+
addOpenListener(listener: OpenListener): string;
|
|
24
|
+
removeOpenListener(id: string): void;
|
|
25
|
+
destroy(): void;
|
|
26
|
+
private startPing;
|
|
27
|
+
private stopPing;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { BaseWs, type MessageListener, type OpenListener, type WsMessage };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { WebSocket } from 'partysocket';
|
|
2
|
+
|
|
3
|
+
class BaseWs {
|
|
4
|
+
ws = null;
|
|
5
|
+
pingTimer = null;
|
|
6
|
+
messageListeners = /* @__PURE__ */ new Map();
|
|
7
|
+
openListeners = /* @__PURE__ */ new Map();
|
|
8
|
+
nextListenerId = 0;
|
|
9
|
+
async connect() {
|
|
10
|
+
if (this.ws) return;
|
|
11
|
+
const ws = new WebSocket(this.getWsUrl(), [], {
|
|
12
|
+
maxReconnectionDelay: 3e4,
|
|
13
|
+
minReconnectionDelay: 1e3,
|
|
14
|
+
reconnectionDelayGrowFactor: 2,
|
|
15
|
+
connectionTimeout: 1e4
|
|
16
|
+
});
|
|
17
|
+
ws.addEventListener("open", () => {
|
|
18
|
+
this.startPing();
|
|
19
|
+
for (const listener of this.openListeners.values()) {
|
|
20
|
+
try {
|
|
21
|
+
listener();
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.warn("Error in ws open listener", error);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
ws.addEventListener("message", (event) => {
|
|
28
|
+
const data = event.data;
|
|
29
|
+
if (typeof data !== "string") return;
|
|
30
|
+
for (const listener of this.messageListeners.values()) {
|
|
31
|
+
try {
|
|
32
|
+
listener(data);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn("Error in ws message listener", error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
ws.addEventListener("close", () => {
|
|
39
|
+
this.stopPing();
|
|
40
|
+
});
|
|
41
|
+
ws.addEventListener("error", () => {
|
|
42
|
+
});
|
|
43
|
+
this.ws = ws;
|
|
44
|
+
await new Promise((resolve, reject) => {
|
|
45
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
46
|
+
resolve();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const onOpen = () => {
|
|
50
|
+
ws.removeEventListener("error", onError);
|
|
51
|
+
resolve();
|
|
52
|
+
};
|
|
53
|
+
const onError = () => {
|
|
54
|
+
ws.removeEventListener("open", onOpen);
|
|
55
|
+
reject(new Error("WebSocket connection failed"));
|
|
56
|
+
};
|
|
57
|
+
ws.addEventListener("open", onOpen, { once: true });
|
|
58
|
+
ws.addEventListener("error", onError, { once: true });
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
disconnect() {
|
|
62
|
+
this.stopPing();
|
|
63
|
+
if (this.ws) {
|
|
64
|
+
this.ws.close();
|
|
65
|
+
this.ws = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
get connected() {
|
|
69
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
70
|
+
}
|
|
71
|
+
send(msg) {
|
|
72
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
73
|
+
this.ws.send(typeof msg === "string" ? msg : JSON.stringify(msg));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
addMessageListener(listener) {
|
|
77
|
+
const id = String(++this.nextListenerId);
|
|
78
|
+
this.messageListeners.set(id, listener);
|
|
79
|
+
return id;
|
|
80
|
+
}
|
|
81
|
+
removeMessageListener(id) {
|
|
82
|
+
this.messageListeners.delete(id);
|
|
83
|
+
}
|
|
84
|
+
addOpenListener(listener) {
|
|
85
|
+
const id = String(++this.nextListenerId);
|
|
86
|
+
this.openListeners.set(id, listener);
|
|
87
|
+
return id;
|
|
88
|
+
}
|
|
89
|
+
removeOpenListener(id) {
|
|
90
|
+
this.openListeners.delete(id);
|
|
91
|
+
}
|
|
92
|
+
destroy() {
|
|
93
|
+
this.disconnect();
|
|
94
|
+
this.messageListeners.clear();
|
|
95
|
+
this.openListeners.clear();
|
|
96
|
+
}
|
|
97
|
+
startPing() {
|
|
98
|
+
this.stopPing();
|
|
99
|
+
const intervalMs = this.getPingIntervalMs();
|
|
100
|
+
if (intervalMs <= 0) return;
|
|
101
|
+
const pingMsg = this.buildPingMessage();
|
|
102
|
+
if (pingMsg === null) return;
|
|
103
|
+
this.pingTimer = setInterval(() => this.send(pingMsg), intervalMs);
|
|
104
|
+
}
|
|
105
|
+
stopPing() {
|
|
106
|
+
if (this.pingTimer) {
|
|
107
|
+
clearInterval(this.pingTimer);
|
|
108
|
+
this.pingTimer = null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export { BaseWs };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BinanceCredentialsDTO } from '@pear-protocol/types';
|
|
2
|
+
import { BaseWs } from '../base-ws.js';
|
|
3
|
+
import { ExchangeWs } from '../exchange-ws.js';
|
|
4
|
+
import { BinanceUserDataWs, BinanceWsApi } from './ws.js';
|
|
5
|
+
import 'partysocket';
|
|
6
|
+
|
|
7
|
+
declare class BinanceExchangeWs extends ExchangeWs {
|
|
8
|
+
userData: BinanceUserDataWs;
|
|
9
|
+
wsApi: BinanceWsApi;
|
|
10
|
+
readonly exchange: "binance";
|
|
11
|
+
constructor(userData: BinanceUserDataWs, wsApi: BinanceWsApi);
|
|
12
|
+
protected connections(): BaseWs[];
|
|
13
|
+
static create(config: {
|
|
14
|
+
demo: boolean;
|
|
15
|
+
credentials: BinanceCredentialsDTO;
|
|
16
|
+
}): Promise<BinanceExchangeWs>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { BinanceExchangeWs };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ExchangeWs } from '../exchange-ws';
|
|
2
|
+
import { createListenKey } from './rest';
|
|
3
|
+
import { BinanceUserDataWs, BinanceWsApi } from './ws';
|
|
4
|
+
|
|
5
|
+
class BinanceExchangeWs extends ExchangeWs {
|
|
6
|
+
constructor(userData, wsApi) {
|
|
7
|
+
super();
|
|
8
|
+
this.userData = userData;
|
|
9
|
+
this.wsApi = wsApi;
|
|
10
|
+
}
|
|
11
|
+
userData;
|
|
12
|
+
wsApi;
|
|
13
|
+
exchange = "binance";
|
|
14
|
+
connections() {
|
|
15
|
+
return [this.userData, this.wsApi];
|
|
16
|
+
}
|
|
17
|
+
static async create(config) {
|
|
18
|
+
const listenKey = await createListenKey(config.credentials.read.api_key, config.demo);
|
|
19
|
+
const userData = new BinanceUserDataWs({ listenKey, demo: config.demo });
|
|
20
|
+
const wsApi = new BinanceWsApi({ demo: config.demo });
|
|
21
|
+
await Promise.all([userData.connect(), wsApi.connect()]);
|
|
22
|
+
return new BinanceExchangeWs(userData, wsApi);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { BinanceExchangeWs };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { BinanceCredentialsDTO } from '@pear-protocol/types';
|
|
2
|
-
import {
|
|
3
|
-
import '../bybit/types.js';
|
|
4
|
-
import '../hyperliquid/types.js';
|
|
5
|
-
import '../okx/types.js';
|
|
2
|
+
import { BinanceRawAccountStatus, BinanceRawPositionRisk } from './types.js';
|
|
6
3
|
|
|
7
4
|
declare function createListenKey(apiKey: string, demo?: boolean): Promise<string>;
|
|
8
5
|
declare function keepAliveListenKey(apiKey: string, demo?: boolean): Promise<void>;
|
|
@@ -10,6 +7,5 @@ declare function fetchAccountStatus(creds: BinanceCredentialsDTO, demo: boolean)
|
|
|
10
7
|
declare function fetchPositionRisk(creds: BinanceCredentialsDTO, symbol: string, demo: boolean): Promise<BinanceRawPositionRisk | undefined>;
|
|
11
8
|
declare function fetchAllPositionRisk(creds: BinanceCredentialsDTO, demo: boolean): Promise<BinanceRawPositionRisk[]>;
|
|
12
9
|
declare function fetchMultiAssetsMargin(creds: BinanceCredentialsDTO, demo: boolean): Promise<boolean>;
|
|
13
|
-
declare function fetchAssetIndex(demo: boolean): Promise<BinanceAssetIndex[]>;
|
|
14
10
|
|
|
15
|
-
export { createListenKey, fetchAccountStatus, fetchAllPositionRisk,
|
|
11
|
+
export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, keepAliveListenKey };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { hmacSha256Hex } from '
|
|
2
|
-
import { BASE_URL_TESTNET, BASE_URL } from './const';
|
|
1
|
+
import { hmacSha256Hex } from '../hmac';
|
|
3
2
|
|
|
3
|
+
const BASE_URL = "https://fapi.binance.com";
|
|
4
|
+
const BASE_URL_TESTNET = "https://testnet.binancefuture.com";
|
|
4
5
|
async function signedGet(creds, path, params, demo) {
|
|
5
6
|
const baseUrl = demo ? BASE_URL_TESTNET : BASE_URL;
|
|
6
7
|
const timestamp = Date.now().toString();
|
|
@@ -53,13 +54,5 @@ async function fetchMultiAssetsMargin(creds, demo) {
|
|
|
53
54
|
const res = await signedGet(creds, "/fapi/v1/multiAssetsMargin", {}, demo);
|
|
54
55
|
return res.multiAssetsMargin;
|
|
55
56
|
}
|
|
56
|
-
async function fetchAssetIndex(demo) {
|
|
57
|
-
const baseUrl = demo ? BASE_URL_TESTNET : BASE_URL;
|
|
58
|
-
const response = await fetch(`${baseUrl}/fapi/v1/assetIndex`);
|
|
59
|
-
if (!response.ok) {
|
|
60
|
-
throw new Error(`Binance API error: ${response.status} ${response.statusText}`);
|
|
61
|
-
}
|
|
62
|
-
return await response.json();
|
|
63
|
-
}
|
|
64
57
|
|
|
65
|
-
export { createListenKey, fetchAccountStatus, fetchAllPositionRisk,
|
|
58
|
+
export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, keepAliveListenKey };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { MarginMode } from '@pear-protocol/types';
|
|
2
|
+
|
|
3
|
+
interface BinanceRawPositionRisk {
|
|
4
|
+
symbol: string;
|
|
5
|
+
leverage: string;
|
|
6
|
+
marginType: MarginMode;
|
|
7
|
+
isAutoAddMargin: string;
|
|
8
|
+
isolatedMargin: string;
|
|
9
|
+
positionAmt: string;
|
|
10
|
+
entryPrice: string;
|
|
11
|
+
breakEvenPrice: string;
|
|
12
|
+
unRealizedProfit: string;
|
|
13
|
+
liquidationPrice: string;
|
|
14
|
+
markPrice: string;
|
|
15
|
+
maxNotionalValue: string;
|
|
16
|
+
positionSide: string;
|
|
17
|
+
notional: string;
|
|
18
|
+
isolatedWallet: string;
|
|
19
|
+
updateTime: number;
|
|
20
|
+
}
|
|
21
|
+
interface BinanceRawAsset {
|
|
22
|
+
asset: string;
|
|
23
|
+
walletBalance: string;
|
|
24
|
+
availableBalance: string;
|
|
25
|
+
unrealizedProfit: string;
|
|
26
|
+
marginBalance: string;
|
|
27
|
+
initialMargin: string;
|
|
28
|
+
maintMargin: string;
|
|
29
|
+
marginAvailable: boolean;
|
|
30
|
+
}
|
|
31
|
+
interface BinanceRawAccountStatus {
|
|
32
|
+
totalWalletBalance: string;
|
|
33
|
+
totalMarginBalance: string;
|
|
34
|
+
availableBalance: string;
|
|
35
|
+
totalUnrealizedProfit: string;
|
|
36
|
+
totalInitialMargin: string;
|
|
37
|
+
totalMaintMargin: string;
|
|
38
|
+
assets?: BinanceRawAsset[];
|
|
39
|
+
}
|
|
40
|
+
interface BinanceMultiAssetsMarginResponse {
|
|
41
|
+
multiAssetsMargin: boolean;
|
|
42
|
+
}
|
|
43
|
+
interface BinanceAssetIndex {
|
|
44
|
+
symbol: string;
|
|
45
|
+
time: number;
|
|
46
|
+
index: string;
|
|
47
|
+
bidBuffer: string;
|
|
48
|
+
askBuffer: string;
|
|
49
|
+
bidRate: string;
|
|
50
|
+
askRate: string;
|
|
51
|
+
autoExchangeBidBuffer: string;
|
|
52
|
+
autoExchangeAskBuffer: string;
|
|
53
|
+
autoExchangeBidRate: string;
|
|
54
|
+
autoExchangeAskRate: string;
|
|
55
|
+
}
|
|
56
|
+
interface BinanceRawUserDataEvent {
|
|
57
|
+
e?: string;
|
|
58
|
+
ac?: {
|
|
59
|
+
s: string;
|
|
60
|
+
l: string;
|
|
61
|
+
};
|
|
62
|
+
a?: {
|
|
63
|
+
B?: unknown[];
|
|
64
|
+
P?: unknown[];
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type { BinanceAssetIndex, BinanceMultiAssetsMarginResponse, BinanceRawAccountStatus, BinanceRawAsset, BinanceRawPositionRisk, BinanceRawUserDataEvent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BinanceCredentialsDTO } from '@pear-protocol/types';
|
|
2
|
+
import { BaseWs } from '../base-ws.js';
|
|
3
|
+
import 'partysocket';
|
|
4
|
+
|
|
5
|
+
type BinanceUserDataWsConfig = {
|
|
6
|
+
listenKey: string;
|
|
7
|
+
demo: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare class BinanceUserDataWs extends BaseWs {
|
|
10
|
+
private readonly listenKey;
|
|
11
|
+
private readonly demo;
|
|
12
|
+
constructor(config: BinanceUserDataWsConfig);
|
|
13
|
+
protected getWsUrl(): string;
|
|
14
|
+
protected getPingIntervalMs(): number;
|
|
15
|
+
protected buildPingMessage(): null;
|
|
16
|
+
}
|
|
17
|
+
type BinanceWsApiConfig = {
|
|
18
|
+
demo: boolean;
|
|
19
|
+
};
|
|
20
|
+
declare class BinanceWsApi extends BaseWs {
|
|
21
|
+
private readonly demo;
|
|
22
|
+
private requestId;
|
|
23
|
+
constructor(config: BinanceWsApiConfig);
|
|
24
|
+
protected getWsUrl(): string;
|
|
25
|
+
protected getPingIntervalMs(): number;
|
|
26
|
+
protected buildPingMessage(): null;
|
|
27
|
+
sendSignedRequest(creds: BinanceCredentialsDTO, method: string, params?: Record<string, unknown>): Promise<number>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { BinanceUserDataWs, type BinanceUserDataWsConfig, BinanceWsApi, type BinanceWsApiConfig };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BaseWs } from '../base-ws';
|
|
2
|
+
import { hmacSha256Hex } from '../hmac';
|
|
3
|
+
|
|
4
|
+
const USER_DATA_URL = "wss://fstream.binance.com/ws";
|
|
5
|
+
const USER_DATA_URL_TESTNET = "wss://stream.binancefuture.com/ws";
|
|
6
|
+
const WS_API_URL = "wss://ws-fapi.binance.com/ws-fapi/v1";
|
|
7
|
+
const WS_API_URL_TESTNET = "wss://testnet.binancefuture.com/ws-fapi/v1";
|
|
8
|
+
class BinanceUserDataWs extends BaseWs {
|
|
9
|
+
listenKey;
|
|
10
|
+
demo;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super();
|
|
13
|
+
this.listenKey = config.listenKey;
|
|
14
|
+
this.demo = config.demo;
|
|
15
|
+
}
|
|
16
|
+
getWsUrl() {
|
|
17
|
+
const base = this.demo ? USER_DATA_URL_TESTNET : USER_DATA_URL;
|
|
18
|
+
return `${base}/${this.listenKey}`;
|
|
19
|
+
}
|
|
20
|
+
getPingIntervalMs() {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
buildPingMessage() {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
class BinanceWsApi extends BaseWs {
|
|
28
|
+
demo;
|
|
29
|
+
requestId = 1;
|
|
30
|
+
constructor(config) {
|
|
31
|
+
super();
|
|
32
|
+
this.demo = config.demo;
|
|
33
|
+
}
|
|
34
|
+
getWsUrl() {
|
|
35
|
+
return this.demo ? WS_API_URL_TESTNET : WS_API_URL;
|
|
36
|
+
}
|
|
37
|
+
getPingIntervalMs() {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
buildPingMessage() {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
async sendSignedRequest(creds, method, params = {}) {
|
|
44
|
+
const id = this.requestId++;
|
|
45
|
+
const timestamp = Date.now();
|
|
46
|
+
const allParams = { ...params, apiKey: creds.read.api_key, timestamp };
|
|
47
|
+
const queryString = Object.keys(allParams).sort().map((k) => `${k}=${allParams[k]}`).join("&");
|
|
48
|
+
const signature = await hmacSha256Hex(creds.read.api_secret, queryString);
|
|
49
|
+
this.send({ id, method, params: { ...allParams, signature } });
|
|
50
|
+
return id;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { BinanceUserDataWs, BinanceWsApi };
|