@pear-protocol/exchanges-sdk 0.6.7 → 0.8.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/index.d.ts +1131 -78
  7. package/dist/sdk.js +9 -2
  8. package/dist/syncer/base.js +36 -14
  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,110 +1,79 @@
1
1
  import BigNumber from 'bignumber.js';
2
2
  import { positionKey } from '../utils';
3
3
  import { QUOTE_ASSET } from './const';
4
- import { LighterAccountType, LighterAccountTier } from './types';
4
+ import { LighterAccountType } from './types';
5
5
 
6
6
  var __defProp = Object.defineProperty;
7
7
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
- function buildAssetBalances(rawAssets) {
9
- const free = {};
10
- const used = {};
11
- const total = {};
12
- const assets = [];
13
- for (const rawAsset of rawAssets) {
14
- const balance = new BigNumber(rawAsset.balance || "0");
15
- const lockedBalance = new BigNumber(rawAsset.locked_balance || "0");
16
- const marginBalance = new BigNumber(rawAsset.margin_balance || "0");
17
- const totalAmount = balance.plus(marginBalance).toFixed();
18
- const freeAmount = BigNumber.max(balance.plus(marginBalance).minus(lockedBalance), 0).toFixed();
19
- free[rawAsset.symbol] = freeAmount;
20
- used[rawAsset.symbol] = rawAsset.locked_balance;
21
- total[rawAsset.symbol] = totalAmount;
22
- assets.push({
23
- asset: rawAsset.symbol,
24
- free: freeAmount,
25
- used: rawAsset.locked_balance,
26
- total: totalAmount,
27
- walletBalance: totalAmount,
28
- usdValue: rawAsset.symbol === QUOTE_ASSET ? totalAmount : "0"
29
- });
30
- }
31
- return {
32
- free,
33
- used,
34
- total,
35
- assets
36
- };
8
+ const USD_DECIMALS = 6;
9
+ const RATE_DECIMALS = 2;
10
+ const FRACTION_DECIMALS = 4;
11
+ const PERCENT_SCALE = 100;
12
+ const MARGIN_FRACTION_SCALE = 1e4;
13
+ const ACCOUNT_MODE_BY_TYPE = {
14
+ [LighterAccountType.CLASSIC]: "lighter:classic",
15
+ [LighterAccountType.UNIFIED]: "lighter:unified"
16
+ };
17
+ function mapLiveBalance(stats, assets, positions, markets, timestamp) {
18
+ const openPositions = Array.from(positions);
19
+ const heldAssets = Array.from(assets);
20
+ const risks = toPositionRisks(openPositions, markets.perpByMarketId);
21
+ const crossEquity = toDecimal(stats.cross.portfolio_value);
22
+ const equity = toEquitySplit(heldAssets, markets.spotPriceBySymbol);
23
+ const perCollateral = toCollateralRows(heldAssets, stats.total.available_balance);
24
+ const crossMaintenance = asMarginRequirement(sumMaintenance(risks, "cross"));
25
+ const isolatedMaintenance = asMarginRequirement(sumMaintenance(risks, "isolated"));
26
+ return toLighterBalance({
27
+ accountMode: ACCOUNT_MODE_BY_TYPE[stats.accountType],
28
+ equity,
29
+ perCollateral,
30
+ unrealizedPnl: signed(sumUnrealizedPnl(openPositions)),
31
+ // The venue states leverage and usage outright here and REST derives them, so each pair meets
32
+ // at one formatter — otherwise the paths would agree only while the venue kept its precision.
33
+ // Usage arrives in percent points and leaves as a fraction, which is the venue's scale changed.
34
+ crossLeverage: rate(toDecimal(stats.cross.leverage)),
35
+ crossMarginUsage: fractionOfPercent(toDecimal(stats.cross.margin_usage)),
36
+ crossMarginRatio: fractionOf(crossMaintenance, crossEquity),
37
+ maintenanceMargin: usdOrNull(sumOf([
38
+ crossMaintenance,
39
+ isolatedMaintenance
40
+ ])),
41
+ availableBalance: stats.total.available_balance,
42
+ timestamp
43
+ });
37
44
  }
38
- __name(buildAssetBalances, "buildAssetBalances");
39
- function mapBalance(summary, assetsByAssetId, rawPositions, marketsByMarketId) {
40
- const { free, used, total, assets } = buildAssetBalances(assetsByAssetId.values());
41
- const portfolioValueBN = new BigNumber(summary.portfolioValue);
42
- const marginUsageFrac = new BigNumber(summary.marginUsagePct).div(100);
43
- const unrealizedPnl = portfolioValueBN.minus(summary.collateral).toFixed(6);
44
- const initialMarginUsed = marginUsageFrac.multipliedBy(portfolioValueBN).toFixed(6);
45
- const positionRisks = calculatePositionRisks(rawPositions, marketsByMarketId);
46
- const maintenanceMargin = positionRisks.reduce((totalMaintenance, positionRisk) => totalMaintenance.plus(positionRisk.maintenance), new BigNumber(0));
47
- const isolatedEquity = positionRisks.reduce((totalEquity, positionRisk) => positionRisk.marginMode === "isolated" ? totalEquity.plus(positionRisk.equity) : totalEquity, new BigNumber(0));
48
- const crossEquity = new BigNumber(summary.crossPortfolioValue ?? portfolioValueBN.minus(isolatedEquity));
49
- const marginRatio = maximumMarginRatio(positionRisks, crossEquity);
50
- return {
51
- accountType: summary.accountType,
52
- accountTier: summary.accountTier,
53
- free,
54
- used,
55
- total,
56
- availableToTrade: summary.availableToTrade,
57
- totalEquity: summary.portfolioValue,
58
- walletBalance: summary.collateral,
59
- unrealizedPnl,
60
- initialMarginUsed,
61
- maintenanceMargin: maintenanceMargin.toFixed(6),
62
- marginRatio,
63
- assets,
64
- timestamp: Date.now()
65
- };
66
- }
67
- __name(mapBalance, "mapBalance");
68
- function buildAccountBalance(account, marketsByMarketId, accountTier) {
69
- const { free, used, total, assets } = buildAssetBalances(account.assets ?? []);
45
+ __name(mapLiveBalance, "mapLiveBalance");
46
+ function mapRestBalance(account, markets, timestamp) {
70
47
  const positions = account.positions ?? [];
71
- const allocatedMargin = sumPositionField(positions, "allocated_margin", (position) => position.margin_mode === 1);
72
- const totalCollateral = new BigNumber(account.collateral).plus(allocatedMargin);
73
- const isolatedInitialMargin = positions.reduce((totalInitialMargin, position) => {
74
- if (position.margin_mode !== 1 || new BigNumber(position.position).isZero()) {
75
- return totalInitialMargin;
76
- }
77
- return totalInitialMargin.plus(new BigNumber(position.position_value).abs().times(position.initial_margin_fraction).div(100));
78
- }, new BigNumber(0));
79
- const initialMargin = new BigNumber(account.cross_initial_margin_requirement).plus(isolatedInitialMargin);
80
- const positionRisks = calculatePositionRisks(positions, marketsByMarketId);
81
- const isolatedMaintenanceMargin = positionRisks.reduce((totalMaintenance, positionRisk) => positionRisk.marginMode === "isolated" ? totalMaintenance.plus(positionRisk.maintenance) : totalMaintenance, new BigNumber(0));
82
- const maintenanceMarginBN = new BigNumber(account.cross_maintenance_margin_requirement).plus(isolatedMaintenanceMargin);
83
- const marginRatio = maximumMarginRatio(positionRisks.filter((positionRisk) => positionRisk.marginMode === "isolated"), new BigNumber(account.cross_asset_value), new BigNumber(account.cross_maintenance_margin_requirement));
84
- const unrealizedPnl = sumPositionField(positions, "unrealized_pnl").toFixed(6);
85
- return {
86
- accountType: toLighterAccountType(account.account_trading_mode),
87
- accountTier,
88
- free,
89
- used,
90
- total,
91
- availableToTrade: account.available_balance,
92
- totalEquity: account.total_asset_value,
93
- walletBalance: totalCollateral.toFixed(6),
94
- unrealizedPnl,
95
- initialMarginUsed: initialMargin.toFixed(6),
96
- maintenanceMargin: maintenanceMarginBN.toFixed(6),
97
- marginRatio,
98
- assets,
99
- timestamp: Date.now()
100
- };
48
+ const assets = account.assets ?? [];
49
+ const risks = toPositionRisks(positions, markets.perpByMarketId);
50
+ const crossEquity = toDecimal(account.cross_asset_value);
51
+ const crossMaintenance = toDecimal(account.cross_maintenance_margin_requirement);
52
+ const isolatedMaintenance = asMarginRequirement(sumMaintenance(risks, "isolated"));
53
+ const equity = toEquitySplit(assets, markets.spotPriceBySymbol);
54
+ const perCollateral = toCollateralRows(assets, account.available_balance);
55
+ return toLighterBalance({
56
+ accountMode: ACCOUNT_MODE_BY_TYPE[toLighterAccountType(account.account_trading_mode)],
57
+ equity,
58
+ perCollateral,
59
+ unrealizedPnl: signed(sumUnrealizedPnl(positions)),
60
+ crossLeverage: multipleOf(sumNotional(risks, "cross"), crossEquity),
61
+ crossMarginUsage: fractionOf(toDecimal(account.cross_initial_margin_requirement), crossEquity),
62
+ crossMarginRatio: fractionOf(crossMaintenance, crossEquity),
63
+ maintenanceMargin: usdOrNull(sumOf([
64
+ crossMaintenance,
65
+ isolatedMaintenance
66
+ ])),
67
+ availableBalance: account.available_balance,
68
+ timestamp
69
+ });
101
70
  }
102
- __name(buildAccountBalance, "buildAccountBalance");
71
+ __name(mapRestBalance, "mapRestBalance");
103
72
  function mapPositions(raw, includeZeroPositions = false) {
104
73
  const positions = /* @__PURE__ */ new Map();
105
74
  const trackedAssets = /* @__PURE__ */ new Map();
106
75
  for (const rawPosition of Object.values(raw)) {
107
- const unsignedSize = new BigNumber(rawPosition.position || "0");
76
+ const unsignedSize = toDecimalOrZero(rawPosition.position);
108
77
  const side = rawPosition.sign === -1 ? "short" : "long";
109
78
  const size = rawPosition.sign === -1 ? unsignedSize.negated() : unsignedSize;
110
79
  const leverage = leverageFromImf(rawPosition.initial_margin_fraction);
@@ -134,7 +103,7 @@ function mapPositions(raw, includeZeroPositions = false) {
134
103
  }
135
104
  __name(mapPositions, "mapPositions");
136
105
  function leverageFromImf(imfPct) {
137
- const pct = new BigNumber(imfPct);
106
+ const pct = toDecimalOrZero(imfPct);
138
107
  if (!pct.isFinite() || pct.lte(0)) return "1";
139
108
  return new BigNumber(100).div(pct).integerValue(BigNumber.ROUND_FLOOR).toFixed();
140
109
  }
@@ -142,76 +111,286 @@ __name(leverageFromImf, "leverageFromImf");
142
111
  function mapMarketDefaultAssetInfo(market) {
143
112
  return {
144
113
  coin: market.symbol,
145
- leverage: leverageFromImf(new BigNumber(market.default_initial_margin_fraction).div(100).toFixed()),
114
+ leverage: leverageFromImf(toDecimalOrZero(market.default_initial_margin_fraction).div(100).toFixed()),
146
115
  marginType: "cross"
147
116
  };
148
117
  }
149
118
  __name(mapMarketDefaultAssetInfo, "mapMarketDefaultAssetInfo");
119
+ function buildSpotPriceIndex(spotMarkets) {
120
+ const priceBySymbol = /* @__PURE__ */ new Map([
121
+ [
122
+ QUOTE_ASSET,
123
+ "1"
124
+ ]
125
+ ]);
126
+ for (const market of spotMarkets) {
127
+ const [baseSymbol] = market.symbol.split("/");
128
+ const price = toDecimal(market.last_trade_price);
129
+ if (baseSymbol === void 0 || market.status !== "active" || price === null || !price.isGreaterThan(0)) {
130
+ continue;
131
+ }
132
+ priceBySymbol.set(baseSymbol, price.toFixed());
133
+ }
134
+ return priceBySymbol;
135
+ }
136
+ __name(buildSpotPriceIndex, "buildSpotPriceIndex");
150
137
  function toLighterAccountType(accountTradingMode) {
151
138
  return accountTradingMode === 1 ? LighterAccountType.UNIFIED : LighterAccountType.CLASSIC;
152
139
  }
153
140
  __name(toLighterAccountType, "toLighterAccountType");
154
- function toLighterAccountTier(userTier) {
155
- switch (userTier?.trim().toLowerCase()) {
156
- case "std":
157
- case "standard":
158
- return LighterAccountTier.STANDARD;
159
- case "premium":
160
- return LighterAccountTier.PREMIUM;
161
- case "plus":
162
- return LighterAccountTier.PLUS;
163
- default:
164
- return void 0;
165
- }
166
- }
167
- __name(toLighterAccountTier, "toLighterAccountTier");
168
- function calculatePositionRisks(rawPositions, marketsByMarketId) {
169
- const positionRisks = [];
170
- for (const rawPosition of rawPositions) {
171
- const market = marketsByMarketId.get(rawPosition.market_id);
172
- if (!market || new BigNumber(rawPosition.position).isZero()) {
141
+ function toLighterBalance(figures) {
142
+ const spotEquity = figures.equity.spot;
143
+ const perpetualsEquity = figures.equity.perpetuals;
144
+ const tradingEquity = sumOf([
145
+ perpetualsEquity,
146
+ spotEquity
147
+ ]);
148
+ const shared = {
149
+ // Public pools, staking and credited LIT are the terms still missing from this sum.
150
+ totalEquity: usdOrNull(tradingEquity),
151
+ withdrawableBalance: usdOrNull(toDecimal(figures.availableBalance)),
152
+ availableMargin: usdOrNull(toDecimal(figures.availableBalance)),
153
+ // Lighter margins every position out of ONE cross pool, so the rows below are holdings inside
154
+ // that pool rather than budgets that could fund each other — only the scalars are spendable.
155
+ poolsShared: true,
156
+ // The rows outlive the scalars: an unpriceable holding nulls `spotEquity`, `tradingEquity` and
157
+ // `totalEquity` above, and still lists here in its own units, because a row needs no price.
158
+ perCollateral: figures.perCollateral,
159
+ timestamp: figures.timestamp,
160
+ tradingEquity: usdOrNull(tradingEquity),
161
+ perpetualsEquity: usdOrNull(perpetualsEquity),
162
+ spotEquity: usdOrNull(spotEquity),
163
+ // Public pool shares have no published net asset value, and staking has no field at all.
164
+ publicPoolsEquity: null,
165
+ stakingEquity: null,
166
+ creditedInLit: null,
167
+ unrealizedPnl: figures.unrealizedPnl,
168
+ crossLeverage: figures.crossLeverage,
169
+ maintenanceMargin: figures.maintenanceMargin,
170
+ crossMarginUsage: figures.crossMarginUsage,
171
+ crossMarginRatio: figures.crossMarginRatio
172
+ };
173
+ switch (figures.accountMode) {
174
+ case "lighter:classic":
175
+ return {
176
+ accountMode: "lighter:classic",
177
+ ...shared
178
+ };
179
+ case "lighter:unified":
180
+ return {
181
+ accountMode: "lighter:unified",
182
+ ...shared,
183
+ // Unified mode's portfolio margin figure appears in neither `/account` nor `user_stats`.
184
+ portfolioMargin: null
185
+ };
186
+ }
187
+ }
188
+ __name(toLighterBalance, "toLighterBalance");
189
+ function toEquitySplit(assets, priceBySymbol) {
190
+ let perpetuals = new BigNumber(0);
191
+ let spot = new BigNumber(0);
192
+ for (const asset of assets) {
193
+ if (asset.symbol === QUOTE_ASSET) {
194
+ perpetuals = sumOf([
195
+ perpetuals,
196
+ toDecimal(asset.margin_balance)
197
+ ]);
198
+ }
199
+ const balance = toDecimal(asset.balance);
200
+ if (balance === null) {
201
+ spot = null;
173
202
  continue;
174
203
  }
175
- const marginMode = rawPosition.margin_mode === 1 ? "isolated" : "cross";
176
- const maintenance = new BigNumber(rawPosition.position_value).abs().times(market.maintenance_margin_fraction).div(1e4);
177
- const equity = marginMode === "isolated" ? new BigNumber(rawPosition.allocated_margin).plus(rawPosition.unrealized_pnl) : new BigNumber(0);
178
- positionRisks.push({
179
- marginMode,
180
- maintenance,
181
- equity
182
- });
204
+ if (balance.isZero()) {
205
+ continue;
206
+ }
207
+ const price = priceBySymbol.get(asset.symbol);
208
+ if (price === void 0) {
209
+ spot = null;
210
+ continue;
211
+ }
212
+ spot = sumOf([
213
+ spot,
214
+ balance.times(price)
215
+ ]);
183
216
  }
184
- return positionRisks;
217
+ return {
218
+ perpetuals,
219
+ spot
220
+ };
185
221
  }
186
- __name(calculatePositionRisks, "calculatePositionRisks");
187
- function maximumMarginRatio(positionRisks, crossEquity, authoritativeCrossMaintenance) {
188
- const calculatedCrossMaintenance = positionRisks.reduce((totalMaintenance, positionRisk) => positionRisk.marginMode === "cross" ? totalMaintenance.plus(positionRisk.maintenance) : totalMaintenance, new BigNumber(0));
189
- const crossMaintenance = authoritativeCrossMaintenance ?? calculatedCrossMaintenance;
190
- let maximumRatio = maintenanceRatio(crossMaintenance, crossEquity);
191
- for (const positionRisk of positionRisks) {
192
- if (positionRisk.marginMode === "isolated") {
193
- maximumRatio = BigNumber.max(maximumRatio, maintenanceRatio(positionRisk.maintenance, positionRisk.equity));
222
+ __name(toEquitySplit, "toEquitySplit");
223
+ function toCollateralRows(assets, availableBalance) {
224
+ const rows = [];
225
+ for (const asset of assets) {
226
+ const held = heldAmount(asset);
227
+ if (held === null || held.isZero()) {
228
+ continue;
194
229
  }
230
+ rows.push(toCollateralRow(asset.symbol, held, availableBalance));
231
+ }
232
+ rows.sort(byAssetName);
233
+ return rows;
234
+ }
235
+ __name(toCollateralRows, "toCollateralRows");
236
+ function heldAmount(asset) {
237
+ return sumOf([
238
+ toDecimal(asset.balance),
239
+ toDecimal(asset.margin_balance)
240
+ ]);
241
+ }
242
+ __name(heldAmount, "heldAmount");
243
+ function toCollateralRow(asset, held, availableBalance) {
244
+ if (asset !== QUOTE_ASSET) {
245
+ return {
246
+ asset,
247
+ equity: held.toFixed(),
248
+ availableMargin: null,
249
+ withdrawable: null
250
+ };
195
251
  }
196
- return maximumRatio.toFixed(4);
252
+ const usable = usdOrNull(toDecimal(availableBalance));
253
+ return {
254
+ asset,
255
+ equity: held.toFixed(),
256
+ availableMargin: usable,
257
+ withdrawable: usable
258
+ };
197
259
  }
198
- __name(maximumMarginRatio, "maximumMarginRatio");
199
- function maintenanceRatio(maintenance, equity) {
200
- if (equity.gt(0)) {
201
- return maintenance.div(equity);
260
+ __name(toCollateralRow, "toCollateralRow");
261
+ function byAssetName(left, right) {
262
+ if (left.asset < right.asset) {
263
+ return -1;
202
264
  }
203
- return maintenance.gt(0) ? new BigNumber(1) : new BigNumber(0);
265
+ if (left.asset > right.asset) {
266
+ return 1;
267
+ }
268
+ return 0;
204
269
  }
205
- __name(maintenanceRatio, "maintenanceRatio");
206
- function sumPositionField(positions, field, include = () => true) {
207
- let total = new BigNumber(0);
270
+ __name(byAssetName, "byAssetName");
271
+ function sumUnrealizedPnl(positions) {
272
+ return sumOf(Array.from(positions, (position) => toDecimal(position.unrealized_pnl)));
273
+ }
274
+ __name(sumUnrealizedPnl, "sumUnrealizedPnl");
275
+ function toPositionRisks(positions, perpByMarketId) {
276
+ const risks = [];
208
277
  for (const position of positions) {
209
- if (include(position)) {
210
- total = total.plus(position[field] || "0");
278
+ if (toDecimalOrZero(position.position).isZero()) {
279
+ continue;
280
+ }
281
+ const positionValue = toDecimal(position.position_value);
282
+ const notional = positionValue === null ? null : positionValue.abs();
283
+ const market = perpByMarketId.get(position.market_id);
284
+ const maintenanceFraction = market === void 0 ? null : toDecimal(market.maintenance_margin_fraction);
285
+ risks.push({
286
+ marginMode: position.margin_mode === 1 ? "isolated" : "cross",
287
+ notional,
288
+ maintenance: notional === null || maintenanceFraction === null ? null : notional.times(maintenanceFraction).div(MARGIN_FRACTION_SCALE)
289
+ });
290
+ }
291
+ return risks;
292
+ }
293
+ __name(toPositionRisks, "toPositionRisks");
294
+ function sumMaintenance(risks, marginMode) {
295
+ return sumOf(risks.filter((risk) => risk.marginMode === marginMode).map((risk) => risk.maintenance));
296
+ }
297
+ __name(sumMaintenance, "sumMaintenance");
298
+ function sumNotional(risks, marginMode) {
299
+ return sumOf(risks.filter((risk) => risk.marginMode === marginMode).map((risk) => risk.notional));
300
+ }
301
+ __name(sumNotional, "sumNotional");
302
+ function sumOf(values) {
303
+ let total = new BigNumber(0);
304
+ for (const value of values) {
305
+ if (value === null) {
306
+ return null;
211
307
  }
308
+ total = total.plus(value);
212
309
  }
213
310
  return total;
214
311
  }
215
- __name(sumPositionField, "sumPositionField");
312
+ __name(sumOf, "sumOf");
313
+ function fractionOf(part, base) {
314
+ if (part === null || base === null) {
315
+ return null;
316
+ }
317
+ if (base.isGreaterThan(0)) {
318
+ return fraction(part.div(base));
319
+ }
320
+ return part.isZero() ? fraction(part) : null;
321
+ }
322
+ __name(fractionOf, "fractionOf");
323
+ function fractionOfPercent(percent) {
324
+ if (percent === null) {
325
+ return null;
326
+ }
327
+ return fraction(percent.div(PERCENT_SCALE));
328
+ }
329
+ __name(fractionOfPercent, "fractionOfPercent");
330
+ function multipleOf(part, base) {
331
+ if (part === null || base === null) {
332
+ return null;
333
+ }
334
+ if (base.isGreaterThan(0)) {
335
+ return rate(part.div(base));
336
+ }
337
+ return part.isZero() ? rate(part) : null;
338
+ }
339
+ __name(multipleOf, "multipleOf");
340
+ function rate(value) {
341
+ if (value === null) {
342
+ return null;
343
+ }
344
+ return value.toFixed(RATE_DECIMALS, BigNumber.ROUND_HALF_UP);
345
+ }
346
+ __name(rate, "rate");
347
+ function fraction(value) {
348
+ return value.toFixed(FRACTION_DECIMALS, BigNumber.ROUND_HALF_UP);
349
+ }
350
+ __name(fraction, "fraction");
351
+ function usd(value) {
352
+ return value.toFixed(USD_DECIMALS, BigNumber.ROUND_DOWN);
353
+ }
354
+ __name(usd, "usd");
355
+ function usdOrNull(value) {
356
+ if (value === null) {
357
+ return null;
358
+ }
359
+ return usd(value);
360
+ }
361
+ __name(usdOrNull, "usdOrNull");
362
+ function asMarginRequirement(value) {
363
+ if (value === null) {
364
+ return null;
365
+ }
366
+ return new BigNumber(value.toFixed(USD_DECIMALS, BigNumber.ROUND_HALF_UP));
367
+ }
368
+ __name(asMarginRequirement, "asMarginRequirement");
369
+ const DECIMAL_PATTERN = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
370
+ function toDecimal(value) {
371
+ if (typeof value === "number") {
372
+ return Number.isFinite(value) ? new BigNumber(value) : null;
373
+ }
374
+ if (typeof value !== "string" || !DECIMAL_PATTERN.test(value)) {
375
+ return null;
376
+ }
377
+ return new BigNumber(value);
378
+ }
379
+ __name(toDecimal, "toDecimal");
380
+ function toDecimalOrZero(value) {
381
+ return toDecimal(value) ?? new BigNumber(0);
382
+ }
383
+ __name(toDecimalOrZero, "toDecimalOrZero");
384
+ function signed(value) {
385
+ if (value === null) {
386
+ return null;
387
+ }
388
+ const fixed = usd(value);
389
+ if (new BigNumber(fixed).isZero()) {
390
+ return "+0";
391
+ }
392
+ return value.isNegative() ? fixed : `+${fixed}`;
393
+ }
394
+ __name(signed, "signed");
216
395
 
217
- export { buildAccountBalance, leverageFromImf, mapBalance, mapMarketDefaultAssetInfo, mapPositions, toLighterAccountTier, toLighterAccountType };
396
+ export { buildSpotPriceIndex, leverageFromImf, mapLiveBalance, mapMarketDefaultAssetInfo, mapPositions, mapRestBalance, toLighterAccountType };