@pear-protocol/exchanges-sdk 0.11.0 → 0.12.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.
- package/dist/index.d.ts +1 -150
- package/dist/sdk.js +0 -8
- package/dist/tracker/binance/tracker.js +0 -21
- package/dist/tracker/bybit/tracker.js +0 -21
- package/dist/tracker/hyperliquid/collateral.js +15 -2
- package/dist/tracker/hyperliquid/tracker.js +1 -69
- package/dist/tracker/index.js +0 -1
- package/dist/tracker/lighter/tracker.js +0 -50
- package/dist/tracker/okx/tracker.js +0 -22
- package/package.json +3 -3
- package/dist/tracker/basket-size.js +0 -194
- package/dist/tracker/hyperliquid/basket-size.js +0 -93
- package/dist/tracker/lighter/basket-size.js +0 -74
package/dist/index.d.ts
CHANGED
|
@@ -1526,98 +1526,6 @@ declare const AccountBalanceCore: z.ZodObject<{
|
|
|
1526
1526
|
/** Human-readable name for each mode, as the venue's own account page labels it. */
|
|
1527
1527
|
declare const ACCOUNT_MODE_LABELS: Record<AccountMode, string>;
|
|
1528
1528
|
|
|
1529
|
-
/**
|
|
1530
|
-
* Mirrors the shipped basket model (`BasketItem` in the frontend): unsigned `weight`
|
|
1531
|
-
* with `side` alongside, weights summing to 100. Sizing ignores `side` — margin
|
|
1532
|
-
* requirement does not depend on direction — and normalises by Σweight.
|
|
1533
|
-
*/
|
|
1534
|
-
declare const BasketLeg: z.ZodObject<{
|
|
1535
|
-
asset: z.ZodString;
|
|
1536
|
-
marginMode: z.ZodEnum<{
|
|
1537
|
-
isolated: "isolated";
|
|
1538
|
-
cross: "cross";
|
|
1539
|
-
}>;
|
|
1540
|
-
leverage: z.ZodString;
|
|
1541
|
-
side: z.ZodEnum<{
|
|
1542
|
-
BUY: "BUY";
|
|
1543
|
-
SELL: "SELL";
|
|
1544
|
-
}>;
|
|
1545
|
-
weight: z.ZodNumber;
|
|
1546
|
-
}, z.core.$strip>;
|
|
1547
|
-
type BasketLeg = z.infer<typeof BasketLeg>;
|
|
1548
|
-
declare const MaxBasketSizeInput: z.ZodObject<{
|
|
1549
|
-
legs: z.ZodArray<z.ZodObject<{
|
|
1550
|
-
asset: z.ZodString;
|
|
1551
|
-
marginMode: z.ZodEnum<{
|
|
1552
|
-
isolated: "isolated";
|
|
1553
|
-
cross: "cross";
|
|
1554
|
-
}>;
|
|
1555
|
-
leverage: z.ZodString;
|
|
1556
|
-
side: z.ZodEnum<{
|
|
1557
|
-
BUY: "BUY";
|
|
1558
|
-
SELL: "SELL";
|
|
1559
|
-
}>;
|
|
1560
|
-
weight: z.ZodNumber;
|
|
1561
|
-
}, z.core.$strip>>;
|
|
1562
|
-
}, z.core.$strip>;
|
|
1563
|
-
type MaxBasketSizeInput = z.infer<typeof MaxBasketSizeInput>;
|
|
1564
|
-
declare const MaxBasketSizeResult: z.ZodObject<{
|
|
1565
|
-
maxNotional: z.ZodNullable<z.ZodString>;
|
|
1566
|
-
legs: z.ZodArray<z.ZodObject<{
|
|
1567
|
-
asset: z.ZodString;
|
|
1568
|
-
notional: z.ZodString;
|
|
1569
|
-
marginRequired: z.ZodString;
|
|
1570
|
-
}, z.core.$strip>>;
|
|
1571
|
-
bindingConstraint: z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1572
|
-
kind: z.ZodLiteral<"cross-pool">;
|
|
1573
|
-
pool: z.ZodString;
|
|
1574
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
1575
|
-
kind: z.ZodLiteral<"isolated-leg">;
|
|
1576
|
-
asset: z.ZodString;
|
|
1577
|
-
}, z.core.$strip>], "kind">>;
|
|
1578
|
-
}, z.core.$strip>;
|
|
1579
|
-
type MaxBasketSizeResult = z.infer<typeof MaxBasketSizeResult>;
|
|
1580
|
-
/**
|
|
1581
|
-
* One basket leg with its leverage already capped by the venue adapter.
|
|
1582
|
-
*
|
|
1583
|
-
* `marginMode` does not enter the maths: an isolated leg gets dedicated margin, but that margin is
|
|
1584
|
-
* still transferred out of the same free balance, so it draws on the same budget as a cross leg.
|
|
1585
|
-
* It is carried so the caller passes its basket through unchanged.
|
|
1586
|
-
*/
|
|
1587
|
-
type CappedBasketLeg = {
|
|
1588
|
-
asset: string;
|
|
1589
|
-
/**
|
|
1590
|
-
* The collateral pool this leg's margin comes out of, as the venue adapter resolves it — a CEX
|
|
1591
|
-
* from the symbol's settlement currency, Hyperliquid from the dex's collateral token. Legs in
|
|
1592
|
-
* different pools cannot fund each other.
|
|
1593
|
-
*/
|
|
1594
|
-
pool: string;
|
|
1595
|
-
/** Unsigned, in the caller's own scale — `computeMaxBasketSize` normalises by Σweight. */
|
|
1596
|
-
weight: number;
|
|
1597
|
-
effectiveLeverage: string;
|
|
1598
|
-
marginMode: MarginMode;
|
|
1599
|
-
/** This leg's own venue ceiling in USD notional; `null` when the venue names no cap. */
|
|
1600
|
-
maxNotional: string | null;
|
|
1601
|
-
};
|
|
1602
|
-
/** The margin budget behind each collateral pool, plus the legs those pools must fund at once. */
|
|
1603
|
-
type MaxBasketSizeCalculation = {
|
|
1604
|
-
/**
|
|
1605
|
-
* Pool id to that pool's USD-equivalent budget. A pool whose collateral is not ~1 USD supplies no
|
|
1606
|
-
* entry until someone adds a price source, and a leg drawing on a missing entry voids the answer.
|
|
1607
|
-
*/
|
|
1608
|
-
budgets: Record<string, string>;
|
|
1609
|
-
legs: CappedBasketLeg[];
|
|
1610
|
-
};
|
|
1611
|
-
/**
|
|
1612
|
-
* The largest gross notional this basket can reach while every leg stays funded at its weight.
|
|
1613
|
-
*
|
|
1614
|
-
* Returns a null size when a pool behind the basket cannot be valued — never a zero, which a
|
|
1615
|
-
* consumer would read as "no collateral" rather than "not known". A basket whose own figures cannot
|
|
1616
|
-
* be sized (see `isSizeableInput`) gets the same null, so this call never throws and never returns a
|
|
1617
|
-
* result that fails `MaxBasketSizeResult`.
|
|
1618
|
-
*/
|
|
1619
|
-
declare function computeMaxBasketSize(input: MaxBasketSizeCalculation): MaxBasketSizeResult;
|
|
1620
|
-
|
|
1621
1529
|
interface AccountPosition {
|
|
1622
1530
|
symbol: string;
|
|
1623
1531
|
side: 'long' | 'short' | 'both';
|
|
@@ -1674,16 +1582,12 @@ interface AccountTracker {
|
|
|
1674
1582
|
disconnect(): Promise<void>;
|
|
1675
1583
|
snapshot(): Promise<AccountSnapshot>;
|
|
1676
1584
|
snapshotAssets(coins: string[]): Promise<AssetSnapshotMap>;
|
|
1677
|
-
/** Sizes a basket over REST alone, so it stays usable without a running socket. */
|
|
1678
|
-
snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1679
1585
|
readonly isConnected: boolean;
|
|
1680
1586
|
readonly isInitialized: boolean;
|
|
1681
1587
|
readonly hasListeners: boolean;
|
|
1682
1588
|
trackBalance(cb: BalanceCallback): () => void;
|
|
1683
1589
|
trackPosition(cb: PositionsCallback): () => void;
|
|
1684
1590
|
trackAsset(asset: string, cb: TrackedAssetCallback): () => void;
|
|
1685
|
-
/** Async: a basket may name assets this tracker has not subscribed to yet. */
|
|
1686
|
-
maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1687
1591
|
getBalance(): AccountBalance | null;
|
|
1688
1592
|
getPositions(): AccountPosition[];
|
|
1689
1593
|
getTrackedAsset(coin: string): TrackedAssetInfo | null;
|
|
@@ -1724,9 +1628,6 @@ declare abstract class BaseAccountTracker implements AccountTracker {
|
|
|
1724
1628
|
private broadcast;
|
|
1725
1629
|
abstract start(): Promise<void>;
|
|
1726
1630
|
abstract snapshot(): Promise<AccountSnapshot>;
|
|
1727
|
-
/** Every venue implements both sizing paths — the live one and the REST one. */
|
|
1728
|
-
abstract maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1729
|
-
abstract snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1730
1631
|
protected abstract fetchAssetSnapshots(coins: string[]): Promise<AssetSnapshotMap>;
|
|
1731
1632
|
abstract get isConnected(): boolean;
|
|
1732
1633
|
protected abstract ensureAssetTracked(asset: string): void;
|
|
@@ -1799,15 +1700,6 @@ declare class BinanceAccountTracker extends BaseAccountTracker {
|
|
|
1799
1700
|
private trackedAssetInfos;
|
|
1800
1701
|
constructor(config: BinanceAccountTrackerConfig);
|
|
1801
1702
|
snapshot(): Promise<AccountSnapshot>;
|
|
1802
|
-
/**
|
|
1803
|
-
* Binance sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
|
|
1804
|
-
* `availableMargin` alone would ship a number no venue field backs. The missing input is
|
|
1805
|
-
* `/fapi/v2/positionRisk` → `maxNotionalValue` (the account's own per-symbol ceiling), with
|
|
1806
|
-
* `/fapi/v1/leverageBracket` behind it for the tier a larger size would fall into.
|
|
1807
|
-
*/
|
|
1808
|
-
maxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1809
|
-
/** The REST path answers the same way — there is no size either way, not a slower one. */
|
|
1810
|
-
snapshotMaxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1811
1703
|
/**
|
|
1812
1704
|
* Stateless one-shot per-asset settings read: `positionRisk` with no symbol returns leverage +
|
|
1813
1705
|
* margin type for every symbol (positioned or not), so any number of assets costs one call.
|
|
@@ -1906,15 +1798,6 @@ declare class BybitAccountTracker extends BaseAccountTracker {
|
|
|
1906
1798
|
private trackedAssetInfos;
|
|
1907
1799
|
constructor(config: BybitAccountTrackerConfig);
|
|
1908
1800
|
snapshot(): Promise<AccountSnapshot>;
|
|
1909
|
-
/**
|
|
1910
|
-
* Bybit sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
|
|
1911
|
-
* `availableMargin` alone would ship a number no venue field backs. The missing input is
|
|
1912
|
-
* `/v5/position/list` → `leverage` (the account's own per-symbol setting), with
|
|
1913
|
-
* `/v5/market/risk-limit` → `maxLeverage` behind it for the tier a larger size would fall into.
|
|
1914
|
-
*/
|
|
1915
|
-
maxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1916
|
-
/** The REST path answers the same way — there is no size either way, not a slower one. */
|
|
1917
|
-
snapshotMaxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
1918
1801
|
/**
|
|
1919
1802
|
* Stateless one-shot per-asset settings read. Bybit has no batch leverage endpoint, but
|
|
1920
1803
|
* `position/list` by settleCoin returns every open position (with leverage) in one call, so
|
|
@@ -2187,13 +2070,6 @@ declare class HyperliquidAccountTracker extends BaseAccountTracker {
|
|
|
2187
2070
|
* per-asset headroom without opening a socket.
|
|
2188
2071
|
*/
|
|
2189
2072
|
snapshot(): Promise<AccountSnapshot>;
|
|
2190
|
-
/** Sizes a basket over REST alone, so the call stays usable without a running socket. */
|
|
2191
|
-
snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
2192
|
-
/**
|
|
2193
|
-
* Sizes a basket against live state, fetching per-asset data for any leg the socket has not
|
|
2194
|
-
* subscribed to — which is exactly why the call is asynchronous on both paths.
|
|
2195
|
-
*/
|
|
2196
|
-
maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
2197
2073
|
/**
|
|
2198
2074
|
* Stateless one-shot per-asset settings read via the `activeAssetData` info request (the REST
|
|
2199
2075
|
* twin of the live WS subscription), one request per coin fired in parallel — the info endpoint
|
|
@@ -2217,10 +2093,6 @@ declare class HyperliquidAccountTracker extends BaseAccountTracker {
|
|
|
2217
2093
|
private routeRaw;
|
|
2218
2094
|
private handle;
|
|
2219
2095
|
private loadPerpMetas;
|
|
2220
|
-
/** Loads the dex metadata a sizing call needs, reusing whatever `start()` already cached. */
|
|
2221
|
-
private loadPerpMetasOnce;
|
|
2222
|
-
/** Loads the token names a pool id is spelled with, reusing whatever `start()` already cached. */
|
|
2223
|
-
private loadCollateralTokenNamesOnce;
|
|
2224
2096
|
/** Reads `activeAssetData` for each coin in parallel, dropping the coins the venue refuses. */
|
|
2225
2097
|
private fetchActiveAssetEvents;
|
|
2226
2098
|
/** The asset set `availableMargin` is the floor over: the locked coins plus every tracked one. */
|
|
@@ -2369,10 +2241,6 @@ declare class LighterAccountTracker extends BaseAccountTracker {
|
|
|
2369
2241
|
* supplies the exchange default for markets absent from the account payload.
|
|
2370
2242
|
*/
|
|
2371
2243
|
protected fetchAssetSnapshots(marketIds: string[]): Promise<AssetSnapshotMap>;
|
|
2372
|
-
/** Sizes a basket against the live account state, falling back to REST before the socket is up. */
|
|
2373
|
-
maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
2374
|
-
/** Sizes a basket over REST alone, so the answer needs no running socket. */
|
|
2375
|
-
snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
2376
2244
|
start(): Promise<void>;
|
|
2377
2245
|
/** Creates one tracker lifecycle while repeated callers share the same pending start. */
|
|
2378
2246
|
private performStart;
|
|
@@ -2417,8 +2285,6 @@ declare class LighterAccountTracker extends BaseAccountTracker {
|
|
|
2417
2285
|
private buildBalance;
|
|
2418
2286
|
/** The market data both read paths join account state against, as the latest read left it. */
|
|
2419
2287
|
private marketContext;
|
|
2420
|
-
/** The account's current per-market settings as the base tracker already holds them. */
|
|
2421
|
-
private liveAccountSettings;
|
|
2422
2288
|
/** Reads the account with credentials when present, or explicitly uses the read-only verifier path. */
|
|
2423
2289
|
private fetchPrimaryAccount;
|
|
2424
2290
|
/** Reads market context under the same account identity as the snapshot that consumes it. */
|
|
@@ -2578,16 +2444,6 @@ declare class OkxAccountTracker extends BaseAccountTracker {
|
|
|
2578
2444
|
private trackedAssetInfos;
|
|
2579
2445
|
constructor(config: OkxAccountTrackerConfig);
|
|
2580
2446
|
snapshot(): Promise<AccountSnapshot>;
|
|
2581
|
-
/**
|
|
2582
|
-
* OKX sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
|
|
2583
|
-
* `availableMargin` alone would ship a number no venue field backs. The missing input is
|
|
2584
|
-
* `GET /api/v5/public/position-tiers` → `maxLever`/`imr` per tier, which the client does not
|
|
2585
|
-
* call — and it is NOT `GET /api/v5/account/position-tiers`, a different endpoint carrying no
|
|
2586
|
-
* margin or leverage data at all.
|
|
2587
|
-
*/
|
|
2588
|
-
maxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
2589
|
-
/** The REST path answers the same way — there is no size either way, not a slower one. */
|
|
2590
|
-
snapshotMaxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
2591
2447
|
/**
|
|
2592
2448
|
* Stateless one-shot per-asset leverage read via `leverage-info`, which accepts up to 20 instIds
|
|
2593
2449
|
* per request (chunked in the client), so N assets cost ceil(N/20) calls. Cross margin only,
|
|
@@ -2685,11 +2541,6 @@ declare class ExchangesSDK {
|
|
|
2685
2541
|
status(connection: ExchangeConnection, options?: ConnectOptions): Promise<AccountStatus>;
|
|
2686
2542
|
createTracker(connection: ExchangeConnection): AccountTracker;
|
|
2687
2543
|
snapshot(tradeAccountId: string): Promise<AccountSnapshot>;
|
|
2688
|
-
/**
|
|
2689
|
-
* Sizes a basket in one shot. Routed to the tracker's REST path on purpose: this entry point
|
|
2690
|
-
* builds a throwaway tracker per call, so it must open no socket.
|
|
2691
|
-
*/
|
|
2692
|
-
maxBasketSize(tradeAccountId: string, input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
|
|
2693
2544
|
snapshotAssets(tradeAccountId: string, coins: string[]): Promise<AssetSnapshotMap>;
|
|
2694
2545
|
createHelper<E extends ExchangeHelperExchange>(exchange: E): ExchangeHelperMap[E];
|
|
2695
2546
|
createSyncer(connection: ExchangeConnection): AccountSyncer;
|
|
@@ -2697,4 +2548,4 @@ declare class ExchangesSDK {
|
|
|
2697
2548
|
private fetchCredentials;
|
|
2698
2549
|
}
|
|
2699
2550
|
|
|
2700
|
-
export { ACCOUNT_MODE_LABELS, ASSET_DECIMALS, AccountBalance, AccountBalanceCore, type AccountHealthStatus, AccountMode, type AccountPosition, type AccountSnapshot, type AccountStatus, type AccountSyncer, type AccountSyncerConfig, type AccountTierCallback, type AccountTracker, type AccountTrackerConfig, type AccountWarningReason, type ApiKey, type ApproveIntegratorParams, type ApproveIntegratorResult, AssetId, type AssetSnapshotMap, type BalanceCallback, BaseAccountSyncer, BaseAccountTracker, BaseWs,
|
|
2551
|
+
export { ACCOUNT_MODE_LABELS, ASSET_DECIMALS, AccountBalance, AccountBalanceCore, type AccountHealthStatus, AccountMode, type AccountPosition, type AccountSnapshot, type AccountStatus, type AccountSyncer, type AccountSyncerConfig, type AccountTierCallback, type AccountTracker, type AccountTrackerConfig, type AccountWarningReason, type ApiKey, type ApproveIntegratorParams, type ApproveIntegratorResult, AssetId, type AssetSnapshotMap, type BalanceCallback, BaseAccountSyncer, BaseAccountTracker, BaseWs, BinanceAccountSyncer, BinanceAccountTracker, type BinanceAccountTrackerConfig, BinanceExchangeWs, BinanceMultiAssetBalance, BinanceSingleAssetBalance, BinanceUserDataWs, type BinanceUserDataWsConfig, BinanceWsApi, type BinanceWsApiConfig, BybitAccountSyncer, BybitAccountTracker, type BybitAccountTrackerConfig, BybitExchangeWs, BybitIsolatedMarginBalance, BybitPortfolioMarginBalance, BybitRegularMarginBalance, BybitWs, type BybitWsConfig, type CctpDepositAddressParams, type CctpDepositAddressResult, type CctpDepositHistorySettlement, type CctpDepositParams, type CctpDepositRegistrationSettlement, type CctpDepositResult, type CctpDepositSettlement, type ChangeAccountTierParams, type ChangeAccountTierResult, type CheckApiWalletParams, CollateralBalance, type ConnectOptions, type ConnectWalletParams, type ConnectWalletResult, type CreateAccountTrackerConfig, type CreateExchangeWsConfig, DecimalString, type EthereumDepositParams, type EthereumDepositResult, type ExchangeConnection, type ExchangeHelperExchange, type ExchangeHelperMap, type ExchangeState, ExchangeWs, type ExchangeWsByConnector, ExchangesSDK, type ExchangesSDKConfig, FUNXYZ_DEPOSIT_MINIMUMS_USD, FUNXYZ_ETHEREUM_MAINNET_CHAIN_ID, FUNXYZ_EVM_CHAIN_NAMES, type FastWithdrawParams, type FastWithdrawResult, type FillSyncOutcome, type FundingSyncOutcome, type FunxyzAccountState, type FunxyzAsset, type FunxyzDepositAddressParams, type FunxyzDepositAddressResult, type FunxyzDepositCredit, type FunxyzDepositParams, type FunxyzDepositResult, type FunxyzDepositSettlement, type FunxyzDepositSource, type FunxyzMarket, HyperliquidAccountSyncer, HyperliquidAccountTracker, type HyperliquidAccountTrackerConfig, HyperliquidDexState, HyperliquidExchangeWs, type HyperliquidHelper, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance, HyperliquidWs, type IntegratorApprovalRequirements, L2_ASSET_DECIMALS, LIGHTER_NATIVE_USDC_ADDRESSES, LIGHTER_USDC_DECIMALS, LighterAccountSyncer, type LighterAccountTier, LighterAccountTracker, type LighterAccountTrackerConfig, type LighterApplyReferralCodeParams, type LighterAuthCredentials, LighterClassicBalance, type LighterDeposit, type LighterDepositAddressParams, type LighterDepositAddressResult, type LighterDepositCredit, type LighterDepositHistoryParams, type LighterDepositNetwork, type LighterDepositPage, type LighterDepositParams, type LighterDepositProgress, type LighterDepositResult, type LighterDepositStatus, type LighterDepositWalletAdapter, LighterExchangeWs, type LighterHelper, type LighterLookupResult, type LighterOnboardingStatus, type LighterReferral, type LighterReferralTotals, type LighterReferralTradeStats, LighterUnifiedBalance, type LighterUserReferrals, type LighterUserReferralsParams, type LighterWithdraw, type LighterWithdrawHistoryParams, type LighterWithdrawPage, type LighterWithdrawStatus, type LighterWithdrawType, LighterWs, type ListExternalFillsParams, type ListExternalFundingParams, type ListExternalPositionsParams, MAX_API_KEY_INDEX, MIN_LIGHTER_CCTP_DEPOSIT_USDC, MIN_LIGHTER_FAST_WITHDRAW_USDC, MIN_USER_API_KEY_INDEX, MarginMode, type MessageListener, OkxAccountSyncer, OkxAccountTracker, type OkxAccountTrackerConfig, OkxExchangeWs, OkxFuturesBalance, OkxMultiCurrencyMarginBalance, OkxPortfolioMarginBalance, OkxSpotBalance, OkxWs, type OkxWsConfig, type OpenListener, type PositionsCallback, RouteType, Side, SignedDecimalString, type StateUpdate, type SupportedCredentials, type TrackedAssetCallback, type TrackedAssetInfo, type WalletClientWithAccount, type WithdrawParams, type WithdrawResult, type WsMessage, createAccountSyncer, createAccountTracker, createExchangeWs, createHyperliquidHelper, createLighterHelper, findAvailableApiKeyIndex, getAssetDecimals, getLighterNativeUsdcAddress, listFunxyzTransferableChains, readFunxyzChainSource, readFunxyzMinimumUsd, retrieveLighterAuthToken, usdcAbi };
|
package/dist/sdk.js
CHANGED
|
@@ -42,14 +42,6 @@ class ExchangesSDK {
|
|
|
42
42
|
const connection = await this.fetchCredentials(tradeAccountId);
|
|
43
43
|
return this.buildTracker(connection).snapshot();
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Sizes a basket in one shot. Routed to the tracker's REST path on purpose: this entry point
|
|
47
|
-
* builds a throwaway tracker per call, so it must open no socket.
|
|
48
|
-
*/
|
|
49
|
-
async maxBasketSize(tradeAccountId, input) {
|
|
50
|
-
const connection = await this.fetchCredentials(tradeAccountId);
|
|
51
|
-
return this.buildTracker(connection).snapshotMaxBasketSize(input);
|
|
52
|
-
}
|
|
53
45
|
async snapshotAssets(tradeAccountId, coins) {
|
|
54
46
|
const connection = await this.fetchCredentials(tradeAccountId);
|
|
55
47
|
return this.buildTracker(connection).snapshotAssets(coins);
|
|
@@ -38,19 +38,6 @@ class BinanceAccountTracker extends BaseAccountTracker {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
-
* Binance sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
|
|
42
|
-
* `availableMargin` alone would ship a number no venue field backs. The missing input is
|
|
43
|
-
* `/fapi/v2/positionRisk` → `maxNotionalValue` (the account's own per-symbol ceiling), with
|
|
44
|
-
* `/fapi/v1/leverageBracket` behind it for the tier a larger size would fall into.
|
|
45
|
-
*/
|
|
46
|
-
async maxBasketSize(_input) {
|
|
47
|
-
return unsizedBasket();
|
|
48
|
-
}
|
|
49
|
-
/** The REST path answers the same way — there is no size either way, not a slower one. */
|
|
50
|
-
async snapshotMaxBasketSize(_input) {
|
|
51
|
-
return unsizedBasket();
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
41
|
* Stateless one-shot per-asset settings read: `positionRisk` with no symbol returns leverage +
|
|
55
42
|
* margin type for every symbol (positioned or not), so any number of assets costs one call.
|
|
56
43
|
*/
|
|
@@ -254,13 +241,5 @@ function toTrackedAssetInfo(coin, risk) {
|
|
|
254
241
|
};
|
|
255
242
|
}
|
|
256
243
|
__name(toTrackedAssetInfo, "toTrackedAssetInfo");
|
|
257
|
-
function unsizedBasket() {
|
|
258
|
-
return {
|
|
259
|
-
maxNotional: null,
|
|
260
|
-
legs: [],
|
|
261
|
-
bindingConstraint: null
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
__name(unsizedBasket, "unsizedBasket");
|
|
265
244
|
|
|
266
245
|
export { BinanceAccountTracker };
|
|
@@ -37,19 +37,6 @@ class BybitAccountTracker extends BaseAccountTracker {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* Bybit sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
|
|
41
|
-
* `availableMargin` alone would ship a number no venue field backs. The missing input is
|
|
42
|
-
* `/v5/position/list` → `leverage` (the account's own per-symbol setting), with
|
|
43
|
-
* `/v5/market/risk-limit` → `maxLeverage` behind it for the tier a larger size would fall into.
|
|
44
|
-
*/
|
|
45
|
-
async maxBasketSize(_input) {
|
|
46
|
-
return unsizedBasket();
|
|
47
|
-
}
|
|
48
|
-
/** The REST path answers the same way — there is no size either way, not a slower one. */
|
|
49
|
-
async snapshotMaxBasketSize(_input) {
|
|
50
|
-
return unsizedBasket();
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
40
|
* Stateless one-shot per-asset settings read. Bybit has no batch leverage endpoint, but
|
|
54
41
|
* `position/list` by settleCoin returns every open position (with leverage) in one call, so
|
|
55
42
|
* only requested symbols without an open position need a per-symbol fill-in read (`?symbol=`
|
|
@@ -426,14 +413,6 @@ function sameWithdrawable(left, right) {
|
|
|
426
413
|
return true;
|
|
427
414
|
}
|
|
428
415
|
__name(sameWithdrawable, "sameWithdrawable");
|
|
429
|
-
function unsizedBasket() {
|
|
430
|
-
return {
|
|
431
|
-
maxNotional: null,
|
|
432
|
-
legs: [],
|
|
433
|
-
bindingConstraint: null
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
__name(unsizedBasket, "unsizedBasket");
|
|
437
416
|
function toTrackedAssetInfo(coin, pos) {
|
|
438
417
|
return {
|
|
439
418
|
coin,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { minimumAvailableToTrade } from './basket-size';
|
|
2
1
|
import { splitDexCoin } from './coin-name';
|
|
2
|
+
import { toDecimal } from './decimal';
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
@@ -44,5 +44,18 @@ function collateralTokenByDex(perDexState) {
|
|
|
44
44
|
return tokenByDex;
|
|
45
45
|
}
|
|
46
46
|
__name(collateralTokenByDex, "collateralTokenByDex");
|
|
47
|
+
function minimumAvailableToTrade(assetData) {
|
|
48
|
+
let floor = null;
|
|
49
|
+
for (const data of assetData) {
|
|
50
|
+
for (const available of data.availableToTrade ?? []) {
|
|
51
|
+
const value = toDecimal(available);
|
|
52
|
+
if (floor === null || value.isLessThan(floor)) {
|
|
53
|
+
floor = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return floor === null ? null : floor.toFixed();
|
|
58
|
+
}
|
|
59
|
+
__name(minimumAvailableToTrade, "minimumAvailableToTrade");
|
|
47
60
|
|
|
48
|
-
export { buildAvailableMarginByToken, buildCollateralTokenNames };
|
|
61
|
+
export { buildAvailableMarginByToken, buildCollateralTokenNames, minimumAvailableToTrade };
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { createExchangeWs } from '../../client';
|
|
2
2
|
import { fetchPerpDexs, fetchInfo, fetchAllMids, fetchUserAbstraction, fetchSpotClearinghouseState, fetchDelegatorSummary, fetchUserVaultEquities, fetchClearinghouseState, fetchActiveAssetData } from '../../client/hyperliquid/rest';
|
|
3
3
|
import { BaseAccountTracker } from '../base';
|
|
4
|
-
import { computeMaxBasketSize } from '../basket-size';
|
|
5
4
|
import { resolveHyperliquidAccountType } from './account-mode';
|
|
6
|
-
import {
|
|
7
|
-
import { buildAvailableMarginByToken, buildCollateralTokenNames } from './collateral';
|
|
5
|
+
import { buildAvailableMarginByToken, buildCollateralTokenNames, minimumAvailableToTrade } from './collateral';
|
|
8
6
|
import { LOCKED_TRACKED_COINS } from './const';
|
|
9
7
|
import { toDecimal, ZERO } from './decimal';
|
|
10
8
|
import { emptyClearinghouseSummary, aggregateClearinghouseStates, mapBalance, mapPositions } from './mapper';
|
|
@@ -96,35 +94,6 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
|
|
|
96
94
|
positions
|
|
97
95
|
};
|
|
98
96
|
}
|
|
99
|
-
/** Sizes a basket over REST alone, so the call stays usable without a running socket. */
|
|
100
|
-
async snapshotMaxBasketSize(input) {
|
|
101
|
-
const [dexToMeta, tokenNames] = await Promise.all([
|
|
102
|
-
this.loadPerpMetasOnce(),
|
|
103
|
-
this.loadCollateralTokenNamesOnce()
|
|
104
|
-
]);
|
|
105
|
-
const assetData = await this.fetchActiveAssetEvents(input.legs.map((leg) => leg.asset));
|
|
106
|
-
return sizeBasket(input, dexToMeta, tokenNames, assetData);
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Sizes a basket against live state, fetching per-asset data for any leg the socket has not
|
|
110
|
-
* subscribed to — which is exactly why the call is asynchronous on both paths.
|
|
111
|
-
*/
|
|
112
|
-
async maxBasketSize(input) {
|
|
113
|
-
const [dexToMeta, tokenNames] = await Promise.all([
|
|
114
|
-
this.loadPerpMetasOnce(),
|
|
115
|
-
this.loadCollateralTokenNamesOnce()
|
|
116
|
-
]);
|
|
117
|
-
const missingCoins = input.legs.map((leg) => leg.asset).filter((asset) => !this.trackedAssetData.has(asset));
|
|
118
|
-
const fetched = await this.fetchActiveAssetEvents(missingCoins);
|
|
119
|
-
const assetData = new Map(fetched);
|
|
120
|
-
for (const leg of input.legs) {
|
|
121
|
-
const cached = this.trackedAssetData.get(leg.asset);
|
|
122
|
-
if (cached) {
|
|
123
|
-
assetData.set(leg.asset, cached);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return sizeBasket(input, dexToMeta, tokenNames, assetData);
|
|
127
|
-
}
|
|
128
97
|
/**
|
|
129
98
|
* Stateless one-shot per-asset settings read via the `activeAssetData` info request (the REST
|
|
130
99
|
* twin of the live WS subscription), one request per coin fired in parallel — the info endpoint
|
|
@@ -363,31 +332,6 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
|
|
|
363
332
|
this.dexToMeta.set(dex, dexMeta);
|
|
364
333
|
});
|
|
365
334
|
}
|
|
366
|
-
/** Loads the dex metadata a sizing call needs, reusing whatever `start()` already cached. */
|
|
367
|
-
async loadPerpMetasOnce() {
|
|
368
|
-
if (this.dexToMeta.size > 0) {
|
|
369
|
-
return this.dexToMeta;
|
|
370
|
-
}
|
|
371
|
-
const [allMetas, perpDexs] = await Promise.all([
|
|
372
|
-
fetchInfo({
|
|
373
|
-
type: "allPerpMetas"
|
|
374
|
-
}, this.demo),
|
|
375
|
-
fetchPerpDexs(this.demo)
|
|
376
|
-
]);
|
|
377
|
-
this.loadPerpMetas(allMetas, perpDexs);
|
|
378
|
-
return this.dexToMeta;
|
|
379
|
-
}
|
|
380
|
-
/** Loads the token names a pool id is spelled with, reusing whatever `start()` already cached. */
|
|
381
|
-
async loadCollateralTokenNamesOnce() {
|
|
382
|
-
if (this.collateralTokenNames.size > 0) {
|
|
383
|
-
return this.collateralTokenNames;
|
|
384
|
-
}
|
|
385
|
-
const spotMeta = await fetchInfo({
|
|
386
|
-
type: "spotMeta"
|
|
387
|
-
}, this.demo);
|
|
388
|
-
this.collateralTokenNames = buildCollateralTokenNames(spotMeta);
|
|
389
|
-
return this.collateralTokenNames;
|
|
390
|
-
}
|
|
391
335
|
/** Reads `activeAssetData` for each coin in parallel, dropping the coins the venue refuses. */
|
|
392
336
|
async fetchActiveAssetEvents(coins) {
|
|
393
337
|
const unique = [
|
|
@@ -558,18 +502,6 @@ class HyperliquidAccountTracker extends BaseAccountTracker {
|
|
|
558
502
|
return result;
|
|
559
503
|
}
|
|
560
504
|
}
|
|
561
|
-
function sizeBasket(input, dexToMeta, tokenNames, assetData) {
|
|
562
|
-
const markets = new Map(input.legs.map((leg) => [
|
|
563
|
-
leg.asset,
|
|
564
|
-
findLegMarket(dexToMeta, tokenNames, leg.asset, assetData.get(leg.asset)?.leverage.value ?? null)
|
|
565
|
-
]));
|
|
566
|
-
const budgets = buildPoolBudgets(input.legs, markets, assetData);
|
|
567
|
-
return computeMaxBasketSize({
|
|
568
|
-
budgets,
|
|
569
|
-
legs: buildCappedLegs(input.legs, markets)
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
__name(sizeBasket, "sizeBasket");
|
|
573
505
|
function buildTrackedAssetInfo(data) {
|
|
574
506
|
return {
|
|
575
507
|
coin: data.coin,
|
package/dist/tracker/index.js
CHANGED
|
@@ -11,7 +11,6 @@ export { OkxAccountTracker } from './okx/tracker';
|
|
|
11
11
|
export { AccountBalance } from './balance';
|
|
12
12
|
export { ACCOUNT_MODE_LABELS, AccountBalanceCore, AccountMode, CollateralBalance, DecimalString, MarginMode, Side, SignedDecimalString } from './balance-core';
|
|
13
13
|
export { BaseAccountTracker } from './base';
|
|
14
|
-
export { BasketLeg, MaxBasketSizeInput, MaxBasketSizeResult, computeMaxBasketSize } from './basket-size';
|
|
15
14
|
export { BinanceMultiAssetBalance, BinanceSingleAssetBalance } from './binance/balance';
|
|
16
15
|
export { BybitIsolatedMarginBalance, BybitPortfolioMarginBalance, BybitRegularMarginBalance } from './bybit/balance';
|
|
17
16
|
export { HyperliquidDexState, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance } from './hyperliquid/balance';
|
|
@@ -3,8 +3,6 @@ import { createExchangeWs } from '../../client';
|
|
|
3
3
|
import { fetchAccount, fetchPublicAccount, fetchOrderBookDetails, fetchPublicOrderBookDetails, fetchAccountLimits } from '../../client/lighter/rest';
|
|
4
4
|
import { retrieveLighterAuthToken } from '../../helper/lighter/auth-token.helper';
|
|
5
5
|
import { BaseAccountTracker } from '../base';
|
|
6
|
-
import { computeMaxBasketSize } from '../basket-size';
|
|
7
|
-
import { toLighterBasketCalculation } from './basket-size';
|
|
8
6
|
import { buildSpotPriceIndex, mapPositions, mapRestBalance, mapMarketDefaultAssetInfo, toLighterAccountType, mapLiveBalance, toLighterAccountTier } from './mapper';
|
|
9
7
|
|
|
10
8
|
var __defProp = Object.defineProperty;
|
|
@@ -141,36 +139,6 @@ class LighterAccountTracker extends BaseAccountTracker {
|
|
|
141
139
|
}
|
|
142
140
|
return result;
|
|
143
141
|
}
|
|
144
|
-
/** Sizes a basket against the live account state, falling back to REST before the socket is up. */
|
|
145
|
-
async maxBasketSize(input) {
|
|
146
|
-
if (!this.stats || this.marketsBySymbol.size === 0) {
|
|
147
|
-
return this.snapshotMaxBasketSize(input);
|
|
148
|
-
}
|
|
149
|
-
return computeMaxBasketSize(toLighterBasketCalculation(input.legs, {
|
|
150
|
-
availableBalance: this.stats.total.available_balance,
|
|
151
|
-
perpBySymbol: this.marketsBySymbol,
|
|
152
|
-
settingsByMarketId: this.liveAccountSettings()
|
|
153
|
-
}));
|
|
154
|
-
}
|
|
155
|
-
/** Sizes a basket over REST alone, so the answer needs no running socket. */
|
|
156
|
-
async snapshotMaxBasketSize(input) {
|
|
157
|
-
const [account, markets] = await Promise.all([
|
|
158
|
-
this.fetchPrimaryAccount(),
|
|
159
|
-
this.fetchMarkets()
|
|
160
|
-
]);
|
|
161
|
-
if (!account) {
|
|
162
|
-
return computeMaxBasketSize({
|
|
163
|
-
budgets: {},
|
|
164
|
-
legs: []
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
const context = {
|
|
168
|
-
availableBalance: account.available_balance,
|
|
169
|
-
perpBySymbol: toMarketsBySymbol(markets.perp),
|
|
170
|
-
settingsByMarketId: toAccountSettings(markets.perp, account.positions ?? [])
|
|
171
|
-
};
|
|
172
|
-
return computeMaxBasketSize(toLighterBasketCalculation(input.legs, context));
|
|
173
|
-
}
|
|
174
142
|
start() {
|
|
175
143
|
if (this.startInFlight) {
|
|
176
144
|
return this.startInFlight;
|
|
@@ -414,17 +382,6 @@ class LighterAccountTracker extends BaseAccountTracker {
|
|
|
414
382
|
spotPriceBySymbol: this.spotPriceBySymbol
|
|
415
383
|
};
|
|
416
384
|
}
|
|
417
|
-
/** The account's current per-market settings as the base tracker already holds them. */
|
|
418
|
-
liveAccountSettings() {
|
|
419
|
-
const settings = /* @__PURE__ */ new Map();
|
|
420
|
-
for (const marketId of this.marketsByMarketId.keys()) {
|
|
421
|
-
const info = this.getTrackedAsset(String(marketId));
|
|
422
|
-
if (info) {
|
|
423
|
-
settings.set(String(marketId), info);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return settings;
|
|
427
|
-
}
|
|
428
385
|
/** Reads the account with credentials when present, or explicitly uses the read-only verifier path. */
|
|
429
386
|
async fetchPrimaryAccount() {
|
|
430
387
|
const authToken = await this.retrieveAuthToken();
|
|
@@ -604,13 +561,6 @@ function toMarketContext(perpMarkets, spotMarkets) {
|
|
|
604
561
|
};
|
|
605
562
|
}
|
|
606
563
|
__name(toMarketContext, "toMarketContext");
|
|
607
|
-
function toMarketsBySymbol(perpMarkets) {
|
|
608
|
-
return new Map(perpMarkets.map((market) => [
|
|
609
|
-
market.symbol,
|
|
610
|
-
market
|
|
611
|
-
]));
|
|
612
|
-
}
|
|
613
|
-
__name(toMarketsBySymbol, "toMarketsBySymbol");
|
|
614
564
|
function toAccountSettings(perpMarkets, positions) {
|
|
615
565
|
const settings = /* @__PURE__ */ new Map();
|
|
616
566
|
for (const market of perpMarkets) {
|
|
@@ -36,20 +36,6 @@ class OkxAccountTracker extends BaseAccountTracker {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* OKX sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
|
|
40
|
-
* `availableMargin` alone would ship a number no venue field backs. The missing input is
|
|
41
|
-
* `GET /api/v5/public/position-tiers` → `maxLever`/`imr` per tier, which the client does not
|
|
42
|
-
* call — and it is NOT `GET /api/v5/account/position-tiers`, a different endpoint carrying no
|
|
43
|
-
* margin or leverage data at all.
|
|
44
|
-
*/
|
|
45
|
-
async maxBasketSize(_input) {
|
|
46
|
-
return unsizedBasket();
|
|
47
|
-
}
|
|
48
|
-
/** The REST path answers the same way — there is no size either way, not a slower one. */
|
|
49
|
-
async snapshotMaxBasketSize(_input) {
|
|
50
|
-
return unsizedBasket();
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
39
|
* Stateless one-shot per-asset leverage read via `leverage-info`, which accepts up to 20 instIds
|
|
54
40
|
* per request (chunked in the client), so N assets cost ceil(N/20) calls. Cross margin only,
|
|
55
41
|
* matching the account-level assumption used everywhere else in this connector.
|
|
@@ -406,13 +392,5 @@ function sameWithdrawable(left, right) {
|
|
|
406
392
|
return true;
|
|
407
393
|
}
|
|
408
394
|
__name(sameWithdrawable, "sameWithdrawable");
|
|
409
|
-
function unsizedBasket() {
|
|
410
|
-
return {
|
|
411
|
-
maxNotional: null,
|
|
412
|
-
legs: [],
|
|
413
|
-
bindingConstraint: null
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
__name(unsizedBasket, "unsizedBasket");
|
|
417
395
|
|
|
418
396
|
export { OkxAccountTracker };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/exchanges-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Pear Protocol Exchanges SDK",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"test:watch": "vitest"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@pear-protocol/core-sdk": "^1.19.
|
|
30
|
+
"@pear-protocol/core-sdk": "^1.19.1",
|
|
31
31
|
"@pear-protocol/types": "^1.30.1",
|
|
32
|
-
"@pear-protocol/utils": "^0.
|
|
32
|
+
"@pear-protocol/utils": "^0.6.0",
|
|
33
33
|
"bignumber.js": "^11.1.1",
|
|
34
34
|
"partysocket": "^1.0.3",
|
|
35
35
|
"viem": "^2.48.8",
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import BigNumber from 'bignumber.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { Side, DecimalString, MarginMode } from './balance-core';
|
|
4
|
-
|
|
5
|
-
var __defProp = Object.defineProperty;
|
|
6
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
-
const BasketLeg = z.object({
|
|
8
|
-
asset: z.string(),
|
|
9
|
-
marginMode: MarginMode,
|
|
10
|
-
leverage: DecimalString,
|
|
11
|
-
side: Side,
|
|
12
|
-
/** Unsigned percentage. The set sums to 100. */
|
|
13
|
-
weight: z.number().positive()
|
|
14
|
-
});
|
|
15
|
-
const MaxBasketSizeInput = z.object({
|
|
16
|
-
legs: z.array(BasketLeg).min(1)
|
|
17
|
-
});
|
|
18
|
-
const MaxBasketSizeResult = z.object({
|
|
19
|
-
/**
|
|
20
|
-
* The whole basket's max GROSS notional in USD — Σ leg notional, not per-side and not per-asset.
|
|
21
|
-
* The basket is atomic, so this is one ceiling for the entire structure.
|
|
22
|
-
* Null when the venue cannot supply a per-asset max leverage (all three CEXs in v1), and null
|
|
23
|
-
* when any leg draws on a pool the caller could not value.
|
|
24
|
-
*/
|
|
25
|
-
maxNotional: DecimalString.nullable(),
|
|
26
|
-
/** Derived from maxNotional — each leg's notional at that basket size. NOT a per-leg maximum. */
|
|
27
|
-
legs: z.array(z.object({
|
|
28
|
-
asset: z.string(),
|
|
29
|
-
notional: DecimalString,
|
|
30
|
-
marginRequired: DecimalString
|
|
31
|
-
})),
|
|
32
|
-
/** Which pool or leg ran out first. */
|
|
33
|
-
bindingConstraint: z.discriminatedUnion("kind", [
|
|
34
|
-
z.object({
|
|
35
|
-
kind: z.literal("cross-pool"),
|
|
36
|
-
pool: z.string()
|
|
37
|
-
}),
|
|
38
|
-
z.object({
|
|
39
|
-
kind: z.literal("isolated-leg"),
|
|
40
|
-
asset: z.string()
|
|
41
|
-
})
|
|
42
|
-
]).nullable()
|
|
43
|
-
});
|
|
44
|
-
function computeMaxBasketSize(input) {
|
|
45
|
-
if (!isSizeableInput(input)) {
|
|
46
|
-
return unsizedResult();
|
|
47
|
-
}
|
|
48
|
-
const budgets = new Map(Object.entries(input.budgets));
|
|
49
|
-
const weightSum = sumWeights(input.legs);
|
|
50
|
-
if (weightSum.isZero()) {
|
|
51
|
-
return unsizedResult();
|
|
52
|
-
}
|
|
53
|
-
const shares = input.legs.map((leg) => toLegShare(leg, weightSum));
|
|
54
|
-
const demandByPool = marginPerUnitNotionalByPool(shares);
|
|
55
|
-
const tightestPool = tightestPoolCeiling(demandByPool, budgets);
|
|
56
|
-
if (tightestPool === null) {
|
|
57
|
-
return unsizedResult();
|
|
58
|
-
}
|
|
59
|
-
const tightestCap = tightestLegCap(shares);
|
|
60
|
-
if (tightestCap === null || tightestPool.size.isLessThanOrEqualTo(tightestCap.size)) {
|
|
61
|
-
return buildResult(tightestPool.size, shares, {
|
|
62
|
-
kind: "cross-pool",
|
|
63
|
-
pool: tightestPool.pool
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return buildResult(tightestCap.size, shares, {
|
|
67
|
-
kind: "isolated-leg",
|
|
68
|
-
asset: tightestCap.asset
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
__name(computeMaxBasketSize, "computeMaxBasketSize");
|
|
72
|
-
function isSizeableInput(input) {
|
|
73
|
-
for (const budget of Object.values(input.budgets)) {
|
|
74
|
-
if (!isUnsignedDecimal(budget)) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
for (const leg of input.legs) {
|
|
79
|
-
if (!isPositiveFinite(leg.weight)) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
if (!isUsableLeverage(leg.effectiveLeverage)) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
if (leg.maxNotional !== null && !isUnsignedDecimal(leg.maxNotional)) {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
__name(isSizeableInput, "isSizeableInput");
|
|
92
|
-
function isUnsignedDecimal(value) {
|
|
93
|
-
return DecimalString.safeParse(value).success;
|
|
94
|
-
}
|
|
95
|
-
__name(isUnsignedDecimal, "isUnsignedDecimal");
|
|
96
|
-
function isPositiveFinite(weight) {
|
|
97
|
-
return Number.isFinite(weight) && weight > 0;
|
|
98
|
-
}
|
|
99
|
-
__name(isPositiveFinite, "isPositiveFinite");
|
|
100
|
-
function isUsableLeverage(effectiveLeverage) {
|
|
101
|
-
if (!isUnsignedDecimal(effectiveLeverage)) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
return !new BigNumber(effectiveLeverage).isZero();
|
|
105
|
-
}
|
|
106
|
-
__name(isUsableLeverage, "isUsableLeverage");
|
|
107
|
-
function sumWeights(legs) {
|
|
108
|
-
let total = new BigNumber(0);
|
|
109
|
-
for (const leg of legs) {
|
|
110
|
-
total = total.plus(String(leg.weight));
|
|
111
|
-
}
|
|
112
|
-
return total;
|
|
113
|
-
}
|
|
114
|
-
__name(sumWeights, "sumWeights");
|
|
115
|
-
function unsizedResult() {
|
|
116
|
-
return {
|
|
117
|
-
maxNotional: null,
|
|
118
|
-
legs: [],
|
|
119
|
-
bindingConstraint: null
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
__name(unsizedResult, "unsizedResult");
|
|
123
|
-
function toLegShare(leg, weightSum) {
|
|
124
|
-
return {
|
|
125
|
-
asset: leg.asset,
|
|
126
|
-
pool: leg.pool,
|
|
127
|
-
share: new BigNumber(String(leg.weight)).div(weightSum),
|
|
128
|
-
imr: new BigNumber(1).div(leg.effectiveLeverage),
|
|
129
|
-
maxNotional: leg.maxNotional === null ? null : new BigNumber(leg.maxNotional)
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
__name(toLegShare, "toLegShare");
|
|
133
|
-
function marginPerUnitNotionalByPool(shares) {
|
|
134
|
-
const demandByPool = /* @__PURE__ */ new Map();
|
|
135
|
-
for (const leg of shares) {
|
|
136
|
-
const running = demandByPool.get(leg.pool) ?? new BigNumber(0);
|
|
137
|
-
demandByPool.set(leg.pool, running.plus(leg.share.times(leg.imr)));
|
|
138
|
-
}
|
|
139
|
-
return demandByPool;
|
|
140
|
-
}
|
|
141
|
-
__name(marginPerUnitNotionalByPool, "marginPerUnitNotionalByPool");
|
|
142
|
-
function tightestPoolCeiling(demandByPool, budgets) {
|
|
143
|
-
let tightest = null;
|
|
144
|
-
for (const [pool, marginPerUnitNotional] of demandByPool) {
|
|
145
|
-
const budget = budgets.get(pool);
|
|
146
|
-
if (budget === void 0) {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
const size = new BigNumber(budget).div(marginPerUnitNotional);
|
|
150
|
-
if (tightest === null || size.isLessThan(tightest.size)) {
|
|
151
|
-
tightest = {
|
|
152
|
-
pool,
|
|
153
|
-
size
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return tightest;
|
|
158
|
-
}
|
|
159
|
-
__name(tightestPoolCeiling, "tightestPoolCeiling");
|
|
160
|
-
function tightestLegCap(shares) {
|
|
161
|
-
let tightest = null;
|
|
162
|
-
for (const leg of shares) {
|
|
163
|
-
if (leg.maxNotional === null) {
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
const size = leg.maxNotional.div(leg.share);
|
|
167
|
-
if (tightest === null || size.isLessThan(tightest.size)) {
|
|
168
|
-
tightest = {
|
|
169
|
-
asset: leg.asset,
|
|
170
|
-
size
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return tightest;
|
|
175
|
-
}
|
|
176
|
-
__name(tightestLegCap, "tightestLegCap");
|
|
177
|
-
function buildResult(size, shares, bindingConstraint) {
|
|
178
|
-
const legs = shares.map((leg) => {
|
|
179
|
-
const notional = size.times(leg.share);
|
|
180
|
-
return {
|
|
181
|
-
asset: leg.asset,
|
|
182
|
-
notional: notional.toFixed(),
|
|
183
|
-
marginRequired: notional.times(leg.imr).toFixed()
|
|
184
|
-
};
|
|
185
|
-
});
|
|
186
|
-
return {
|
|
187
|
-
maxNotional: size.toFixed(),
|
|
188
|
-
legs,
|
|
189
|
-
bindingConstraint
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
__name(buildResult, "buildResult");
|
|
193
|
-
|
|
194
|
-
export { BasketLeg, MaxBasketSizeInput, MaxBasketSizeResult, computeMaxBasketSize };
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import BigNumber from 'bignumber.js';
|
|
2
|
-
import { splitDexCoin } from './coin-name';
|
|
3
|
-
import { toDecimal } from './decimal';
|
|
4
|
-
|
|
5
|
-
var __defProp = Object.defineProperty;
|
|
6
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
-
const UNRESOLVED_POOL = "";
|
|
8
|
-
function buildCappedLegs(legs, markets) {
|
|
9
|
-
return legs.map((leg) => {
|
|
10
|
-
const market = markets.get(leg.asset) ?? null;
|
|
11
|
-
const leverage = effectiveLeverage(leg.leverage, market);
|
|
12
|
-
return {
|
|
13
|
-
asset: leg.asset,
|
|
14
|
-
pool: market?.pool ?? UNRESOLVED_POOL,
|
|
15
|
-
weight: leg.weight,
|
|
16
|
-
effectiveLeverage: leverage,
|
|
17
|
-
marginMode: leg.marginMode,
|
|
18
|
-
maxNotional: tierBoundaryNotional(market?.marginTiers ?? null, leverage)
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
__name(buildCappedLegs, "buildCappedLegs");
|
|
23
|
-
function buildPoolBudgets(legs, markets, assetData) {
|
|
24
|
-
const dataByPool = /* @__PURE__ */ new Map();
|
|
25
|
-
for (const leg of legs) {
|
|
26
|
-
const pool = markets.get(leg.asset)?.pool ?? null;
|
|
27
|
-
const data = assetData.get(leg.asset);
|
|
28
|
-
if (pool === null || data === void 0) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const bucket = dataByPool.get(pool) ?? [];
|
|
32
|
-
bucket.push(data);
|
|
33
|
-
dataByPool.set(pool, bucket);
|
|
34
|
-
}
|
|
35
|
-
const budgets = {};
|
|
36
|
-
for (const [pool, data] of dataByPool) {
|
|
37
|
-
const floor = minimumAvailableToTrade(data);
|
|
38
|
-
if (floor !== null) {
|
|
39
|
-
budgets[pool] = floor;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return budgets;
|
|
43
|
-
}
|
|
44
|
-
__name(buildPoolBudgets, "buildPoolBudgets");
|
|
45
|
-
function effectiveLeverage(requestedLeverage, market) {
|
|
46
|
-
let leverage = toDecimal(requestedLeverage);
|
|
47
|
-
if (market?.venueMaxLeverage !== null && market?.venueMaxLeverage !== void 0) {
|
|
48
|
-
leverage = BigNumber.min(leverage, market.venueMaxLeverage);
|
|
49
|
-
}
|
|
50
|
-
if (market?.accountLeverage !== null && market?.accountLeverage !== void 0) {
|
|
51
|
-
leverage = BigNumber.min(leverage, market.accountLeverage);
|
|
52
|
-
}
|
|
53
|
-
return BigNumber.max(leverage, 1).toFixed();
|
|
54
|
-
}
|
|
55
|
-
__name(effectiveLeverage, "effectiveLeverage");
|
|
56
|
-
function tierBoundaryNotional(marginTiers, effectiveLeverage2) {
|
|
57
|
-
if (!marginTiers) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const leverage = toDecimal(effectiveLeverage2);
|
|
61
|
-
const boundary = marginTiers.find((tier) => leverage.isGreaterThan(tier.maxLeverage));
|
|
62
|
-
return boundary ? boundary.lowerBound : null;
|
|
63
|
-
}
|
|
64
|
-
__name(tierBoundaryNotional, "tierBoundaryNotional");
|
|
65
|
-
function minimumAvailableToTrade(assetData) {
|
|
66
|
-
let floor = null;
|
|
67
|
-
for (const data of assetData) {
|
|
68
|
-
for (const available of data.availableToTrade ?? []) {
|
|
69
|
-
const value = toDecimal(available);
|
|
70
|
-
if (floor === null || value.isLessThan(floor)) {
|
|
71
|
-
floor = value;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return floor === null ? null : floor.toFixed();
|
|
76
|
-
}
|
|
77
|
-
__name(minimumAvailableToTrade, "minimumAvailableToTrade");
|
|
78
|
-
function findLegMarket(dexToMeta, tokenNames, coin, accountLeverage) {
|
|
79
|
-
const { dex, bareCoin } = splitDexCoin(coin);
|
|
80
|
-
const meta = dexToMeta.get(dex);
|
|
81
|
-
const asset = meta?.universe.find((candidate) => candidate.name === coin || candidate.name === bareCoin);
|
|
82
|
-
const marginTiers = meta?.marginTables.find(([id]) => id === asset?.marginTableId)?.[1].marginTiers;
|
|
83
|
-
const collateralToken = meta?.collateralToken;
|
|
84
|
-
return {
|
|
85
|
-
pool: collateralToken === void 0 ? null : tokenNames.get(collateralToken) ?? null,
|
|
86
|
-
venueMaxLeverage: asset?.maxLeverage ?? null,
|
|
87
|
-
accountLeverage,
|
|
88
|
-
marginTiers: marginTiers?.length ? marginTiers : null
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
__name(findLegMarket, "findLegMarket");
|
|
92
|
-
|
|
93
|
-
export { buildCappedLegs, buildPoolBudgets, effectiveLeverage, findLegMarket, minimumAvailableToTrade, tierBoundaryNotional };
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import BigNumber from 'bignumber.js';
|
|
2
|
-
import { QUOTE_ASSET } from './const';
|
|
3
|
-
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
-
const MARGIN_FRACTION_SCALE = 1e4;
|
|
7
|
-
const CROSS_POOL = QUOTE_ASSET;
|
|
8
|
-
const DECIMAL_PATTERN = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
|
|
9
|
-
function toLighterBasketCalculation(legs, context) {
|
|
10
|
-
const capped = [];
|
|
11
|
-
for (const leg of legs) {
|
|
12
|
-
const market = context.perpBySymbol.get(leg.asset);
|
|
13
|
-
if (!market) {
|
|
14
|
-
return {
|
|
15
|
-
budgets: {},
|
|
16
|
-
legs: []
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
capped.push(toCappedLeg(leg, market, context.settingsByMarketId.get(String(market.market_id))));
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
// Every leg draws on the same pool, so one entry carries the whole basket's budget.
|
|
23
|
-
budgets: {
|
|
24
|
-
[CROSS_POOL]: context.availableBalance
|
|
25
|
-
},
|
|
26
|
-
legs: capped
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
__name(toLighterBasketCalculation, "toLighterBasketCalculation");
|
|
30
|
-
function toCappedLeg(leg, market, currentSetting) {
|
|
31
|
-
return {
|
|
32
|
-
asset: leg.asset,
|
|
33
|
-
pool: CROSS_POOL,
|
|
34
|
-
weight: leg.weight,
|
|
35
|
-
effectiveLeverage: effectiveLeverage(leg.leverage, market, currentSetting),
|
|
36
|
-
marginMode: leg.marginMode,
|
|
37
|
-
maxNotional: null
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
__name(toCappedLeg, "toCappedLeg");
|
|
41
|
-
function effectiveLeverage(requested, market, currentSetting) {
|
|
42
|
-
const accountLeverage = currentSetting === void 0 ? defaultAccountLeverage(market) : toDecimal(currentSetting.leverage);
|
|
43
|
-
const granted = BigNumber.min(toDecimal(requested), venueMaxLeverage(market), accountLeverage);
|
|
44
|
-
return granted.isFinite() && granted.isGreaterThan(0) ? granted.toFixed() : "1";
|
|
45
|
-
}
|
|
46
|
-
__name(effectiveLeverage, "effectiveLeverage");
|
|
47
|
-
function venueMaxLeverage(market) {
|
|
48
|
-
return leverageFromMarginFraction(market.min_initial_margin_fraction);
|
|
49
|
-
}
|
|
50
|
-
__name(venueMaxLeverage, "venueMaxLeverage");
|
|
51
|
-
function defaultAccountLeverage(market) {
|
|
52
|
-
return leverageFromMarginFraction(market.default_initial_margin_fraction).integerValue(BigNumber.ROUND_FLOOR);
|
|
53
|
-
}
|
|
54
|
-
__name(defaultAccountLeverage, "defaultAccountLeverage");
|
|
55
|
-
function leverageFromMarginFraction(marginFraction) {
|
|
56
|
-
const fraction = toDecimal(marginFraction);
|
|
57
|
-
if (!fraction.isGreaterThan(0)) {
|
|
58
|
-
return new BigNumber(1);
|
|
59
|
-
}
|
|
60
|
-
return new BigNumber(MARGIN_FRACTION_SCALE).div(fraction);
|
|
61
|
-
}
|
|
62
|
-
__name(leverageFromMarginFraction, "leverageFromMarginFraction");
|
|
63
|
-
function toDecimal(value) {
|
|
64
|
-
if (typeof value === "number") {
|
|
65
|
-
return Number.isFinite(value) ? new BigNumber(value) : new BigNumber(0);
|
|
66
|
-
}
|
|
67
|
-
if (typeof value !== "string" || !DECIMAL_PATTERN.test(value)) {
|
|
68
|
-
return new BigNumber(0);
|
|
69
|
-
}
|
|
70
|
-
return new BigNumber(value);
|
|
71
|
-
}
|
|
72
|
-
__name(toDecimal, "toDecimal");
|
|
73
|
-
|
|
74
|
-
export { toLighterBasketCalculation, venueMaxLeverage };
|