@pear-protocol/exchanges-sdk 0.6.6 → 0.7.0

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 (41) hide show
  1. package/README.md +105 -25
  2. package/dist/client/bybit/rest.js +8 -1
  3. package/dist/client/hyperliquid/rest.js +21 -1
  4. package/dist/client/lighter/rest.js +6 -12
  5. package/dist/client/okx/rest.js +5 -1
  6. package/dist/helper/lighter/helper.js +6 -2
  7. package/dist/index.d.ts +1112 -74
  8. package/dist/sdk.js +9 -2
  9. package/dist/tracker/balance-core.js +89 -0
  10. package/dist/tracker/balance.js +25 -0
  11. package/dist/tracker/basket-size.js +194 -0
  12. package/dist/tracker/binance/balance.js +11 -0
  13. package/dist/tracker/binance/mapper.js +87 -42
  14. package/dist/tracker/binance/tracker.js +29 -14
  15. package/dist/tracker/binance/types.js +0 -6
  16. package/dist/tracker/bybit/balance.js +14 -0
  17. package/dist/tracker/bybit/mapper.js +109 -52
  18. package/dist/tracker/bybit/tracker.js +242 -33
  19. package/dist/tracker/bybit/types.js +0 -7
  20. package/dist/tracker/hyperliquid/account-mode.js +14 -0
  21. package/dist/tracker/hyperliquid/balance.js +184 -0
  22. package/dist/tracker/hyperliquid/basket-size.js +93 -0
  23. package/dist/tracker/hyperliquid/coin-name.js +18 -0
  24. package/dist/tracker/hyperliquid/collateral.js +48 -0
  25. package/dist/tracker/hyperliquid/decimal.js +28 -0
  26. package/dist/tracker/hyperliquid/mapper.js +399 -176
  27. package/dist/tracker/hyperliquid/spot-pricing.js +106 -0
  28. package/dist/tracker/hyperliquid/tracker.js +237 -78
  29. package/dist/tracker/hyperliquid/types.js +1 -7
  30. package/dist/tracker/index.js +9 -7
  31. package/dist/tracker/lighter/balance.js +87 -0
  32. package/dist/tracker/lighter/basket-size.js +74 -0
  33. package/dist/tracker/lighter/mapper.js +324 -145
  34. package/dist/tracker/lighter/tracker.js +228 -117
  35. package/dist/tracker/lighter/types.js +1 -7
  36. package/dist/tracker/okx/balance.js +17 -0
  37. package/dist/tracker/okx/mapper.js +156 -58
  38. package/dist/tracker/okx/tracker.js +228 -29
  39. package/dist/tracker/okx/types.js +0 -8
  40. package/dist/tracker/types.js +0 -24
  41. package/package.json +7 -9
@@ -1,105 +1,109 @@
1
1
  import BigNumber from 'bignumber.js';
2
2
  import { positionKey } from '../utils';
3
- import { COLLATERAL_TOKEN_MAP, HyperliquidAccountType } from './types';
3
+ import { toDecimal, ZERO } from './decimal';
4
+ import { HyperliquidAccountType } from './types';
4
5
 
5
6
  var __defProp = Object.defineProperty;
6
7
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
- const LEGACY_COLLATERAL_TOKEN_MAP = new Map(Object.entries(COLLATERAL_TOKEN_MAP).map(([token, coin]) => [
8
- Number(token),
9
- coin
10
- ]));
11
8
  const FULL_UTILIZATION_RATIO = "1.0000";
9
+ const RATIO_DECIMALS = 4;
10
+ const USDC_TOKEN_INDEX = 0;
12
11
  const ZERO_COLLATERAL_SUMMARY = {
13
- accountValue: "0",
14
- totalMarginUsed: "0",
15
12
  crossMaintenanceMarginUsed: "0",
16
- isolatedMaintenanceMarginUsed: "0",
17
13
  isolatedMarginUsed: "0",
18
- withdrawable: "0",
19
- totalUnrealizedPnl: "0"
14
+ accountValue: "0",
15
+ withdrawable: "0"
20
16
  };
21
17
  function aggregateClearinghouseStates(states) {
22
- const summary = {
23
- accountValue: "0",
24
- totalMarginUsed: "0",
25
- totalRawUsd: "0",
26
- crossMaintenanceMarginUsed: "0",
27
- isolatedMaintenanceMarginUsed: "0",
28
- maximumStandardMarginRatio: "0.0000",
29
- withdrawable: "0",
30
- totalUnrealizedPnl: "0",
31
- byCollateralToken: /* @__PURE__ */ new Map()
32
- };
18
+ const summary = emptyClearinghouseSummary();
33
19
  const assetPositions = [];
34
20
  for (const dexState of states) {
35
21
  const state = dexState.state;
36
22
  const positions = state.assetPositions ?? [];
37
- const unrealizedPnl = sum(positions.map((position) => position.position.unrealizedPnl));
38
- const isolatedRisks = positions.flatMap((position) => {
23
+ const unrealizedPnl = sum(positions.map((position) => position.position.unrealizedPnl)).toFixed();
24
+ const isolatedMarginUsed = sum(positions.flatMap((position) => {
39
25
  if (position.position.leverage.type !== "isolated") {
40
26
  return [];
41
27
  }
42
28
  return [
43
- {
44
- collateral: position.position.marginUsed,
45
- maintenance: isolatedMaintenanceMargin(dexState, position)
46
- }
29
+ position.position.marginUsed
47
30
  ];
48
- });
49
- const isolatedMaintenanceMarginUsed = sum(isolatedRisks.map((risk) => risk.maintenance));
50
- const isolatedMarginUsed = sum(isolatedRisks.map((risk) => risk.collateral));
51
- let dexMaximumMarginRatio = ratio(state.crossMaintenanceMarginUsed, state.crossMarginSummary.accountValue);
52
- for (const isolatedRisk of isolatedRisks) {
53
- dexMaximumMarginRatio = BigNumber.max(dexMaximumMarginRatio, ratio(isolatedRisk.maintenance, isolatedRisk.collateral)).toFixed(4);
54
- }
31
+ })).toFixed();
55
32
  summary.accountValue = add(summary.accountValue, state.marginSummary.accountValue);
56
- summary.totalMarginUsed = add(summary.totalMarginUsed, state.marginSummary.totalMarginUsed);
57
- summary.totalRawUsd = add(summary.totalRawUsd, state.marginSummary.totalRawUsd);
33
+ summary.crossAccountValue = add(summary.crossAccountValue, state.crossMarginSummary.accountValue);
34
+ summary.crossNotional = add(summary.crossNotional, state.crossMarginSummary.totalNtlPos);
58
35
  summary.crossMaintenanceMarginUsed = add(summary.crossMaintenanceMarginUsed, state.crossMaintenanceMarginUsed);
59
- summary.isolatedMaintenanceMarginUsed = add(summary.isolatedMaintenanceMarginUsed, isolatedMaintenanceMarginUsed);
60
- summary.maximumStandardMarginRatio = BigNumber.max(summary.maximumStandardMarginRatio, dexMaximumMarginRatio).toFixed(4);
61
36
  summary.withdrawable = add(summary.withdrawable, state.withdrawable);
62
37
  summary.totalUnrealizedPnl = add(summary.totalUnrealizedPnl, unrealizedPnl);
63
38
  assetPositions.push(...positions);
39
+ summary.perDexState.push({
40
+ dex: dexState.dex,
41
+ collateralToken: dexState.collateralToken,
42
+ accountValue: state.marginSummary.accountValue,
43
+ crossNotional: state.crossMarginSummary.totalNtlPos,
44
+ crossMaintenanceMargin: state.crossMaintenanceMarginUsed,
45
+ withdrawable: state.withdrawable
46
+ });
64
47
  if (dexState.collateralToken === null) {
65
48
  continue;
66
49
  }
67
50
  const collateral = summary.byCollateralToken.get(dexState.collateralToken) ?? ZERO_COLLATERAL_SUMMARY;
68
51
  summary.byCollateralToken.set(dexState.collateralToken, {
69
- accountValue: add(collateral.accountValue, state.marginSummary.accountValue),
70
- totalMarginUsed: add(collateral.totalMarginUsed, state.marginSummary.totalMarginUsed),
71
52
  crossMaintenanceMarginUsed: add(collateral.crossMaintenanceMarginUsed, state.crossMaintenanceMarginUsed),
72
- isolatedMaintenanceMarginUsed: add(collateral.isolatedMaintenanceMarginUsed, isolatedMaintenanceMarginUsed),
73
53
  isolatedMarginUsed: add(collateral.isolatedMarginUsed, isolatedMarginUsed),
74
- withdrawable: add(collateral.withdrawable, state.withdrawable),
75
- totalUnrealizedPnl: add(collateral.totalUnrealizedPnl, unrealizedPnl)
54
+ accountValue: add(collateral.accountValue, state.marginSummary.accountValue),
55
+ withdrawable: add(collateral.withdrawable, state.withdrawable)
76
56
  });
77
57
  }
58
+ summary.perDexState.sort(byDexName);
78
59
  return {
79
60
  summary,
80
61
  assetPositions
81
62
  };
82
63
  }
83
64
  __name(aggregateClearinghouseStates, "aggregateClearinghouseStates");
84
- function mapBalance(summary, spotCoinBalances, accountType, collateralTokenMap = LEGACY_COLLATERAL_TOKEN_MAP) {
85
- if (!accountType) {
65
+ function byDexName(left, right) {
66
+ if (left.dex === right.dex) {
67
+ return 0;
68
+ }
69
+ return left.dex < right.dex ? -1 : 1;
70
+ }
71
+ __name(byDexName, "byDexName");
72
+ function emptyClearinghouseSummary() {
73
+ return {
74
+ accountValue: "0",
75
+ crossAccountValue: "0",
76
+ crossNotional: "0",
77
+ crossMaintenanceMarginUsed: "0",
78
+ withdrawable: "0",
79
+ totalUnrealizedPnl: "0",
80
+ byCollateralToken: /* @__PURE__ */ new Map(),
81
+ perDexState: []
82
+ };
83
+ }
84
+ __name(emptyClearinghouseSummary, "emptyClearinghouseSummary");
85
+ function mapBalance(context) {
86
+ if (!context.accountType) {
86
87
  return null;
87
88
  }
88
- switch (accountType) {
89
+ switch (context.accountType) {
89
90
  case HyperliquidAccountType.DEFAULT:
90
91
  case HyperliquidAccountType.DISABLED:
91
- return mapStandardBalance(summary, accountType, collateralTokenMap);
92
+ return mapStandardBalance(context);
92
93
  case HyperliquidAccountType.UNIFIED_ACCOUNT:
94
+ return mapUnifiedBalance(context);
93
95
  case HyperliquidAccountType.PORTFOLIO_MARGIN:
94
- return mapUnifiedBalance(summary, spotCoinBalances, accountType, collateralTokenMap);
96
+ return mapPortfolioMarginBalance(context);
95
97
  }
96
98
  }
97
99
  __name(mapBalance, "mapBalance");
98
100
  function mapPositions(raw) {
99
101
  const positions = /* @__PURE__ */ new Map();
100
- for (const assetPosition of raw) {
102
+ for (const assetPosition of [
103
+ ...raw
104
+ ].sort(byPositionKey)) {
101
105
  const position = assetPosition.position;
102
- const size = new BigNumber(position.szi || "0");
106
+ const size = toDecimal(position.szi);
103
107
  if (size.isZero()) {
104
108
  continue;
105
109
  }
@@ -119,167 +123,386 @@ function mapPositions(raw) {
119
123
  return positions;
120
124
  }
121
125
  __name(mapPositions, "mapPositions");
122
- function mapStandardBalance(summary, accountType, collateralTokenMap) {
123
- const free = {};
124
- const used = {};
125
- const total = {};
126
- const assets = [];
127
- for (const [token, collateral] of summary.byCollateralToken) {
128
- const coin = collateralTokenMap.get(token);
129
- if (!coin) {
130
- continue;
131
- }
132
- free[coin] = collateral.withdrawable;
133
- used[coin] = collateral.totalMarginUsed;
134
- total[coin] = collateral.accountValue;
135
- assets.push({
136
- asset: coin,
137
- free: collateral.withdrawable,
138
- used: collateral.totalMarginUsed,
139
- total: collateral.accountValue,
140
- walletBalance: subtract(collateral.accountValue, collateral.totalUnrealizedPnl),
141
- usdValue: collateral.accountValue
142
- });
126
+ function byPositionKey(left, right) {
127
+ const leftCoin = left.position.coin || "";
128
+ const rightCoin = right.position.coin || "";
129
+ if (leftCoin === rightCoin) {
130
+ return 0;
143
131
  }
144
- const maintenanceMargin = add(summary.crossMaintenanceMarginUsed, summary.isolatedMaintenanceMarginUsed);
132
+ return leftCoin < rightCoin ? -1 : 1;
133
+ }
134
+ __name(byPositionKey, "byPositionKey");
135
+ function mapStandardBalance(context) {
136
+ const spotEquity = spotEquityOf(context);
137
+ const perpsEquity = subtract(context.summary.accountValue, context.summary.totalUnrealizedPnl);
138
+ const stakedEquity = stakedEquityOf(context);
145
139
  return {
146
- accountType,
147
- free,
148
- used,
149
- total,
150
- availableToTrade: summary.withdrawable,
151
- totalEquity: summary.accountValue,
152
- walletBalance: subtract(summary.accountValue, summary.totalUnrealizedPnl),
153
- unrealizedPnl: summary.totalUnrealizedPnl,
154
- initialMarginUsed: summary.totalMarginUsed,
155
- maintenanceMargin,
156
- marginRatio: summary.maximumStandardMarginRatio,
157
- assets,
158
- timestamp: Date.now()
140
+ accountMode: "hyperliquid:standard",
141
+ totalEquity: addAllKnown([
142
+ spotEquity,
143
+ perpsEquity,
144
+ context.vaultEquity,
145
+ stakedEquity
146
+ ]),
147
+ // Standard withdrawable is the perps wallet's free margin, summed across dexes.
148
+ withdrawableBalance: context.summary.withdrawable,
149
+ availableMargin: context.availableMargin,
150
+ // Separate budgets, not holdings in one pool: a USDH dex cannot spend the USDC beside it.
151
+ poolsShared: false,
152
+ perCollateral: perpsCollateralRows(context),
153
+ spotEquity,
154
+ perpsEquity: signed(perpsEquity),
155
+ vaultEquity: context.vaultEquity,
156
+ stakedEquity,
157
+ unrealizedPnl: signed(context.summary.totalUnrealizedPnl),
158
+ // Cross-only: isolated legs carry their own margin and liquidate independently.
159
+ maintenanceMargin: context.summary.crossMaintenanceMarginUsed,
160
+ // Risk levers against ACCOUNT VALUE, not the cash above: one pool, one quotient, one division.
161
+ crossMarginRatio: ratio(context.summary.crossMaintenanceMarginUsed, context.summary.crossAccountValue),
162
+ crossAccountLeverage: accountLeverage(context.summary.crossNotional, context.summary.crossAccountValue),
163
+ perDexState: context.summary.perDexState,
164
+ timestamp: context.timestamp
159
165
  };
160
166
  }
161
167
  __name(mapStandardBalance, "mapStandardBalance");
162
- function mapUnifiedBalance(summary, spotCoinBalances, accountType, collateralTokenMap) {
163
- const free = {};
164
- const used = {};
165
- const total = {};
166
- const assets = [];
167
- let totalEquity = new BigNumber(0);
168
- let availableToTrade = new BigNumber(0);
169
- for (const balance of spotCoinBalances.values()) {
170
- if (!collateralTokenMap.has(balance.token)) {
168
+ function mapUnifiedBalance(context) {
169
+ const tradingEquity = spotEquityOf(context);
170
+ const stakedEquity = stakedEquityOf(context);
171
+ const portfolioValue = addAllKnown([
172
+ tradingEquity,
173
+ context.vaultEquity,
174
+ stakedEquity
175
+ ]);
176
+ return {
177
+ accountMode: "hyperliquid:unified",
178
+ totalEquity: portfolioValue,
179
+ // What may leave is the spot balance net of the margin the perps side has locked.
180
+ withdrawableBalance: freeCollateral(context),
181
+ availableMargin: context.availableMargin,
182
+ // Separate budgets, not holdings in one pool: a USDH dex cannot spend the USDC beside it.
183
+ poolsShared: false,
184
+ perCollateral: spotCollateralRows(context),
185
+ tradingEquity,
186
+ vaultEquity: context.vaultEquity,
187
+ stakedEquity,
188
+ portfolioValue,
189
+ unrealizedPnl: signed(context.summary.totalUnrealizedPnl),
190
+ // Cross-only: adding isolated maintenance overstated the figure by 2x against the venue's UI.
191
+ perpsMaintenanceMargin: context.summary.crossMaintenanceMarginUsed,
192
+ unifiedAccountRatio: crossRatioAgainstFreeSpot(context),
193
+ unifiedAccountLeverage: unifiedAccountLeverage(context),
194
+ perDexState: context.summary.perDexState,
195
+ timestamp: context.timestamp
196
+ };
197
+ }
198
+ __name(mapUnifiedBalance, "mapUnifiedBalance");
199
+ function mapPortfolioMarginBalance(context) {
200
+ const tradingEquity = spotEquityOf(context);
201
+ const stakedEquity = stakedEquityOf(context);
202
+ return {
203
+ accountMode: "hyperliquid:portfolioMargin",
204
+ // Total Equity is the only figure that adds what sits outside trading back on.
205
+ totalEquity: addAllKnown([
206
+ tradingEquity,
207
+ context.vaultEquity,
208
+ stakedEquity
209
+ ]),
210
+ withdrawableBalance: settlementAvailableBalance(context),
211
+ availableMargin: context.availableMargin,
212
+ // Holdings in one pool, not separate budgets: PM lends against every coin at its LTV weight.
213
+ poolsShared: true,
214
+ perCollateral: portfolioMarginCollateralRows(context),
215
+ tradingEquity,
216
+ vaultEquity: context.vaultEquity,
217
+ stakedEquity,
218
+ portfolioValue: tradingEquity,
219
+ unrealizedPnl: signed(context.summary.totalUnrealizedPnl),
220
+ perpsMaintenanceMargin: context.summary.crossMaintenanceMarginUsed,
221
+ portfolioMarginRatio: wireMarginRatio(context),
222
+ portfolioAccountLeverage: portfolioAccountLeverage(context),
223
+ borrowCapUsed: worstBorrowCapUsed(context),
224
+ perDexState: context.summary.perDexState,
225
+ timestamp: context.timestamp
226
+ };
227
+ }
228
+ __name(mapPortfolioMarginBalance, "mapPortfolioMarginBalance");
229
+ function settlementAvailableBalance(context) {
230
+ const settlement = spotBalanceForToken(context, USDC_TOKEN_INDEX);
231
+ if (!settlement) {
232
+ return null;
233
+ }
234
+ const available = toDecimal(settlement.total).minus(toDecimal(settlement.hold));
235
+ return available.isNegative() ? null : available.toFixed();
236
+ }
237
+ __name(settlementAvailableBalance, "settlementAvailableBalance");
238
+ function portfolioMarginCollateralRows(context) {
239
+ const rows = [];
240
+ for (const balance of context.spotBalances.values()) {
241
+ const total = toDecimal(balance.total);
242
+ rows.push({
243
+ asset: balance.coin,
244
+ equity: signed(total.toFixed()),
245
+ availableMargin: context.availableMarginByToken.get(balance.token) ?? null,
246
+ withdrawable: signed(total.minus(toDecimal(balance.hold)).toFixed()),
247
+ ltv: balance.ltv === null ? null : toDecimal(balance.ltv).toFixed(),
248
+ borrowCapUsed: borrowCapUsedFor(context, balance.token)
249
+ });
250
+ }
251
+ rows.sort(byRowAsset);
252
+ return rows;
253
+ }
254
+ __name(portfolioMarginCollateralRows, "portfolioMarginCollateralRows");
255
+ function byRowAsset(left, right) {
256
+ if (left.asset === right.asset) {
257
+ return 0;
258
+ }
259
+ return left.asset < right.asset ? -1 : 1;
260
+ }
261
+ __name(byRowAsset, "byRowAsset");
262
+ function borrowCapUsedFor(context, token) {
263
+ const used = toDecimal(context.borrowRatioByToken.get(token));
264
+ return used.isZero() ? null : used.toFixed(RATIO_DECIMALS);
265
+ }
266
+ __name(borrowCapUsedFor, "borrowCapUsedFor");
267
+ function worstBorrowCapUsed(context) {
268
+ let worst = ZERO;
269
+ for (const ratio2 of context.borrowRatioByToken.values()) {
270
+ worst = BigNumber.max(worst, toDecimal(ratio2));
271
+ }
272
+ return worst.isZero() ? null : worst.toFixed(RATIO_DECIMALS);
273
+ }
274
+ __name(worstBorrowCapUsed, "worstBorrowCapUsed");
275
+ function wireMarginRatio(context) {
276
+ if (context.portfolioMarginRatio === null) {
277
+ return null;
278
+ }
279
+ return toDecimal(context.portfolioMarginRatio).toFixed(RATIO_DECIMALS);
280
+ }
281
+ __name(wireMarginRatio, "wireMarginRatio");
282
+ function portfolioAccountLeverage(context) {
283
+ const collateral = haircutCollateral(context);
284
+ if (collateral === null) {
285
+ return null;
286
+ }
287
+ return accountLeverage(context.summary.crossNotional, collateral);
288
+ }
289
+ __name(portfolioAccountLeverage, "portfolioAccountLeverage");
290
+ function haircutCollateral(context) {
291
+ let collateral = ZERO;
292
+ for (const balance of context.spotBalances.values()) {
293
+ const total = toDecimal(balance.total);
294
+ if (total.isZero()) {
171
295
  continue;
172
296
  }
173
- const collateral = summary.byCollateralToken.get(balance.token) ?? ZERO_COLLATERAL_SUMMARY;
174
- const coinUsed = balance.hold;
175
- const coinFree = subtract(balance.total, coinUsed);
176
- const coinWalletBalance = subtract(balance.total, collateral.totalUnrealizedPnl);
177
- free[balance.coin] = coinFree;
178
- used[balance.coin] = coinUsed;
179
- total[balance.coin] = balance.total;
180
- totalEquity = totalEquity.plus(balance.total);
181
- availableToTrade = availableToTrade.plus(balance.availableAfterMaintenance ?? "0");
182
- assets.push({
183
- asset: balance.coin,
184
- free: coinFree,
185
- used: coinUsed,
186
- total: balance.total,
187
- walletBalance: coinWalletBalance,
188
- usdValue: balance.total
297
+ if (balance.ltv === null && total.isPositive()) {
298
+ continue;
299
+ }
300
+ const mid = context.prices.midForToken(balance.token);
301
+ if (mid === null) {
302
+ return null;
303
+ }
304
+ collateral = collateral.plus(weightedCollateral(total.times(toDecimal(mid)), balance.ltv));
305
+ }
306
+ return collateral.toFixed();
307
+ }
308
+ __name(haircutCollateral, "haircutCollateral");
309
+ function weightedCollateral(usd, ltv) {
310
+ if (usd.isNegative()) {
311
+ return usd;
312
+ }
313
+ if (ltv === null) {
314
+ return ZERO;
315
+ }
316
+ const weight = toDecimal(ltv);
317
+ return weight.isZero() ? usd : usd.times(weight);
318
+ }
319
+ __name(weightedCollateral, "weightedCollateral");
320
+ function perpsCollateralRows(context) {
321
+ const rows = collateralPools(context).map((pool) => ({
322
+ asset: pool.asset,
323
+ equity: pool.collateral.accountValue,
324
+ availableMargin: context.availableMarginByToken.get(pool.token) ?? null,
325
+ withdrawable: pool.collateral.withdrawable
326
+ }));
327
+ return fundedRows(rows);
328
+ }
329
+ __name(perpsCollateralRows, "perpsCollateralRows");
330
+ function spotCollateralRows(context) {
331
+ const rows = collateralPools(context).map((pool) => {
332
+ const balance = spotBalanceForToken(context, pool.token);
333
+ const total = toDecimal(balance?.total);
334
+ const locked = BigNumber.max(toDecimal(balance?.hold), ZERO);
335
+ return {
336
+ asset: pool.asset,
337
+ equity: total.toFixed(),
338
+ availableMargin: context.availableMarginByToken.get(pool.token) ?? null,
339
+ withdrawable: total.minus(locked).toFixed()
340
+ };
341
+ });
342
+ return fundedRows(rows);
343
+ }
344
+ __name(spotCollateralRows, "spotCollateralRows");
345
+ function fundedRows(rows) {
346
+ return rows.filter(isFundedRow);
347
+ }
348
+ __name(fundedRows, "fundedRows");
349
+ function isFundedRow(row) {
350
+ const figures = [
351
+ row.equity,
352
+ row.withdrawable,
353
+ row.availableMargin ?? "0"
354
+ ];
355
+ return figures.some((figure) => !toDecimal(figure).isZero());
356
+ }
357
+ __name(isFundedRow, "isFundedRow");
358
+ function collateralPools(context) {
359
+ const pools = [];
360
+ for (const [token, collateral] of context.summary.byCollateralToken) {
361
+ const asset = context.collateralTokenNames.get(token);
362
+ if (asset === void 0) {
363
+ continue;
364
+ }
365
+ pools.push({
366
+ token,
367
+ asset,
368
+ collateral
189
369
  });
190
370
  }
191
- const maintenanceMargin = add(summary.crossMaintenanceMarginUsed, summary.isolatedMaintenanceMarginUsed);
192
- const totalEquityValue = totalEquity.toFixed();
193
- const walletBalance = subtract(totalEquityValue, summary.totalUnrealizedPnl);
194
- return {
195
- accountType,
196
- free,
197
- used,
198
- total,
199
- availableToTrade: availableToTrade.toFixed(),
200
- // Unified spot totals are the authoritative equity balances and already include perp PnL.
201
- totalEquity: totalEquityValue,
202
- walletBalance,
203
- unrealizedPnl: summary.totalUnrealizedPnl,
204
- initialMarginUsed: summary.totalMarginUsed,
205
- maintenanceMargin,
206
- marginRatio: unifiedMarginRatio(summary, spotCoinBalances),
207
- assets,
208
- timestamp: Date.now()
209
- };
371
+ pools.sort(byAssetName);
372
+ return pools;
210
373
  }
211
- __name(mapUnifiedBalance, "mapUnifiedBalance");
212
- function unifiedMarginRatio(summary, spotCoinBalances) {
213
- let maximumRatio = new BigNumber(0);
214
- for (const [token, collateral] of summary.byCollateralToken) {
215
- const spotBalance = [
216
- ...spotCoinBalances.values()
217
- ].find((balance) => balance.token === token);
218
- const available = new BigNumber(spotBalance?.total ?? "0").minus(collateral.isolatedMarginUsed);
219
- maximumRatio = BigNumber.max(maximumRatio, ratio(collateral.crossMaintenanceMarginUsed, available.toFixed()));
374
+ __name(collateralPools, "collateralPools");
375
+ function byAssetName(left, right) {
376
+ if (left.asset === right.asset) {
377
+ return 0;
220
378
  }
221
- return maximumRatio.toFixed(4);
379
+ return left.asset < right.asset ? -1 : 1;
222
380
  }
223
- __name(unifiedMarginRatio, "unifiedMarginRatio");
224
- function isolatedMaintenanceMargin(dexState, assetPosition) {
225
- const position = assetPosition.position;
226
- if (position.leverage.type !== "isolated" || position.maxLeverage <= 0) {
381
+ __name(byAssetName, "byAssetName");
382
+ function spotEquityOf(context) {
383
+ const valuation = context.prices.valueBalances(context.spotBalances.values());
384
+ if (valuation.unpricedCoins.length > 0) {
385
+ return null;
386
+ }
387
+ return valuation.equity;
388
+ }
389
+ __name(spotEquityOf, "spotEquityOf");
390
+ function stakedEquityOf(context) {
391
+ const staked = toDecimal(context.stakedHype);
392
+ if (staked.isZero()) {
227
393
  return "0";
228
394
  }
229
- const notional = new BigNumber(position.positionValue).abs();
230
- const marginTiers = findMarginTiers(dexState, position.coin);
231
- const firstMarginTier = marginTiers?.[0];
232
- if (!marginTiers || !firstMarginTier || marginTiers.some((tier) => tier.maxLeverage <= 0)) {
233
- return notional.div(position.maxLeverage).div(2).toFixed();
395
+ const mid = context.prices.midForCoin("HYPE");
396
+ if (mid === null) {
397
+ return null;
234
398
  }
235
- let activeTier = firstMarginTier;
236
- let maintenanceDeduction = new BigNumber(0);
237
- for (const nextTier of marginTiers.slice(1)) {
238
- if (notional.lt(nextTier.lowerBound)) {
239
- break;
399
+ return staked.times(toDecimal(mid)).toFixed();
400
+ }
401
+ __name(stakedEquityOf, "stakedEquityOf");
402
+ function freeCollateral(context) {
403
+ let free = ZERO;
404
+ for (const token of context.summary.byCollateralToken.keys()) {
405
+ const balance = spotBalanceForToken(context, token);
406
+ if (!balance) {
407
+ continue;
240
408
  }
241
- const previousRate = maintenanceMarginRate(activeTier.maxLeverage);
242
- const nextRate = maintenanceMarginRate(nextTier.maxLeverage);
243
- maintenanceDeduction = maintenanceDeduction.plus(new BigNumber(nextTier.lowerBound).times(nextRate.minus(previousRate)));
244
- activeTier = nextTier;
409
+ const locked = BigNumber.max(toDecimal(balance.hold), ZERO);
410
+ free = free.plus(toDecimal(balance.total).minus(locked));
245
411
  }
246
- return notional.times(maintenanceMarginRate(activeTier.maxLeverage)).minus(maintenanceDeduction).toFixed();
412
+ return free.toFixed();
247
413
  }
248
- __name(isolatedMaintenanceMargin, "isolatedMaintenanceMargin");
249
- function findMarginTiers(dexState, coin) {
250
- if (!dexState.meta) {
414
+ __name(freeCollateral, "freeCollateral");
415
+ function crossRatioAgainstFreeSpot(context) {
416
+ let maximumRatio = ZERO;
417
+ for (const [token, collateral] of context.summary.byCollateralToken) {
418
+ const spotTotal = spotBalanceForToken(context, token)?.total ?? "0";
419
+ const available = toDecimal(spotTotal).minus(toDecimal(collateral.isolatedMarginUsed));
420
+ maximumRatio = BigNumber.max(maximumRatio, ratio(collateral.crossMaintenanceMarginUsed, available.toFixed()));
421
+ }
422
+ return maximumRatio.toFixed(RATIO_DECIMALS);
423
+ }
424
+ __name(crossRatioAgainstFreeSpot, "crossRatioAgainstFreeSpot");
425
+ function unifiedAccountLeverage(context) {
426
+ const collateral = collateralValue(context);
427
+ if (collateral === null) {
251
428
  return null;
252
429
  }
253
- const unprefixedCoin = dexState.dex && coin.startsWith(`${dexState.dex}:`) ? coin.slice(dexState.dex.length + 1) : coin;
254
- const asset = dexState.meta.universe.find((candidate) => candidate.name === coin || candidate.name === unprefixedCoin);
255
- const marginTable = dexState.meta.marginTables.find(([id]) => id === asset?.marginTableId)?.[1];
256
- return marginTable?.marginTiers.length ? marginTable.marginTiers : null;
430
+ return accountLeverage(context.summary.crossNotional, collateral);
257
431
  }
258
- __name(findMarginTiers, "findMarginTiers");
259
- function maintenanceMarginRate(maxLeverage) {
260
- return new BigNumber(1).div(maxLeverage).div(2);
432
+ __name(unifiedAccountLeverage, "unifiedAccountLeverage");
433
+ function collateralValue(context) {
434
+ let collateral = ZERO;
435
+ for (const token of context.summary.byCollateralToken.keys()) {
436
+ const balance = spotBalanceForToken(context, token);
437
+ const total = toDecimal(balance?.total);
438
+ if (total.isZero()) {
439
+ continue;
440
+ }
441
+ const mid = context.prices.midForToken(token);
442
+ if (mid === null) {
443
+ return null;
444
+ }
445
+ collateral = collateral.plus(total.times(toDecimal(mid)));
446
+ }
447
+ return collateral.toFixed();
448
+ }
449
+ __name(collateralValue, "collateralValue");
450
+ function spotBalanceForToken(context, token) {
451
+ for (const balance of context.spotBalances.values()) {
452
+ if (balance.token === token) {
453
+ return balance;
454
+ }
455
+ }
456
+ return null;
457
+ }
458
+ __name(spotBalanceForToken, "spotBalanceForToken");
459
+ function accountLeverage(crossNotional, equity) {
460
+ const equityValue = toDecimal(equity);
461
+ if (!equityValue.isGreaterThan(0)) {
462
+ return "0.0000";
463
+ }
464
+ return toDecimal(crossNotional).div(equityValue).toFixed(RATIO_DECIMALS);
261
465
  }
262
- __name(maintenanceMarginRate, "maintenanceMarginRate");
466
+ __name(accountLeverage, "accountLeverage");
263
467
  function add(left, right) {
264
- return new BigNumber(left || "0").plus(right || "0").toFixed();
468
+ return toDecimal(left).plus(toDecimal(right)).toFixed();
265
469
  }
266
470
  __name(add, "add");
471
+ function addAllKnown(values) {
472
+ let total = ZERO;
473
+ for (const value of values) {
474
+ if (value === null) {
475
+ return null;
476
+ }
477
+ total = total.plus(toDecimal(value));
478
+ }
479
+ return total.toFixed();
480
+ }
481
+ __name(addAllKnown, "addAllKnown");
267
482
  function subtract(left, right) {
268
- return new BigNumber(left || "0").minus(right || "0").toFixed();
483
+ return toDecimal(left).minus(toDecimal(right)).toFixed();
269
484
  }
270
485
  __name(subtract, "subtract");
271
486
  function sum(values) {
272
- return values.reduce((total, value) => total.plus(value || "0"), new BigNumber(0)).toFixed();
487
+ return values.reduce((total, value) => total.plus(toDecimal(value)), ZERO);
273
488
  }
274
489
  __name(sum, "sum");
490
+ function signed(value) {
491
+ const amount = toDecimal(value);
492
+ if (amount.isZero()) {
493
+ return "+0";
494
+ }
495
+ return amount.isNegative() ? amount.toFixed() : `+${amount.toFixed()}`;
496
+ }
497
+ __name(signed, "signed");
275
498
  function ratio(numerator, denominator) {
276
- const numeratorValue = new BigNumber(numerator || "0");
277
- const denominatorValue = new BigNumber(denominator || "0");
499
+ const numeratorValue = toDecimal(numerator);
500
+ const denominatorValue = toDecimal(denominator);
278
501
  if (denominatorValue.gt(0)) {
279
- return numeratorValue.div(denominatorValue).toFixed(4);
502
+ return numeratorValue.div(denominatorValue).toFixed(RATIO_DECIMALS);
280
503
  }
281
504
  return numeratorValue.gt(0) ? FULL_UTILIZATION_RATIO : "0.0000";
282
505
  }
283
506
  __name(ratio, "ratio");
284
507
 
285
- export { aggregateClearinghouseStates, mapBalance, mapPositions };
508
+ export { aggregateClearinghouseStates, emptyClearinghouseSummary, mapBalance, mapPositions };