@pear-protocol/symmio-client 0.2.23 → 0.2.26

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.
@@ -24934,11 +24934,10 @@ function useSymmMarkets(params) {
24934
24934
  ...params?.query,
24935
24935
  queryKey: symmKeys.markets(chainId, searchText),
24936
24936
  queryFn: () => {
24937
- const marketsApi = symmCoreClient.markets;
24938
24937
  if (searchText) {
24939
- return marketsApi.search(searchText, chainId);
24938
+ return symmCoreClient.markets.search(searchText, { chainId });
24940
24939
  }
24941
- return marketsApi.list({ pageSize: "500", chainId });
24940
+ return symmCoreClient.markets.list({ pageSize: "1000", chainId });
24942
24941
  },
24943
24942
  enabled: internalEnabled && (params?.query?.enabled ?? true)
24944
24943
  });
@@ -25002,634 +25001,128 @@ function useSymmHedgerMarkets(params) {
25002
25001
  enabled: internalEnabled && consumerEnabled
25003
25002
  });
25004
25003
  }
25005
- function useSymmFunding(params) {
25006
- const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25007
- const chainId = params?.chainId ?? ctxChainId;
25008
- const internalEnabled = !!symmCoreClient;
25009
- return useQuery({
25010
- ...params?.query,
25011
- queryKey: symmKeys.fundingRates(chainId),
25012
- queryFn: () => symmCoreClient.funding.getRates({ chainId }),
25013
- enabled: internalEnabled && (params?.query?.enabled ?? true)
25014
- });
25015
- }
25016
- function useSymmFundingHistory(params) {
25017
- const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25018
- const chainId = params.chainId ?? ctxChainId;
25019
- const {
25020
- period,
25021
- longSymbols,
25022
- shortSymbols,
25023
- interval,
25024
- weightMode,
25025
- includeContributions
25026
- } = params;
25027
- const internalEnabled = !!symmCoreClient;
25028
- const request = {
25029
- chainId,
25030
- period,
25031
- longSymbols,
25032
- shortSymbols,
25004
+
25005
+ // src/utils/binance-api.ts
25006
+ var BINANCE_FAPI_BASE = "https://fapi.binance.com";
25007
+ async function fetchMarkPriceKlines(symbol, interval, startTime, endTime, limit = 1500) {
25008
+ const params = new URLSearchParams({
25009
+ symbol,
25033
25010
  interval,
25034
- weightMode,
25035
- includeContributions
25036
- };
25037
- return useQuery({
25038
- ...params.query,
25039
- queryKey: symmKeys.fundingHistory(request),
25040
- queryFn: () => symmCoreClient.funding.getHistory(request),
25041
- enabled: internalEnabled && (params.query?.enabled ?? true)
25011
+ startTime: String(startTime),
25012
+ endTime: String(endTime),
25013
+ limit: String(Math.min(limit, 1500))
25042
25014
  });
25015
+ const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/markPriceKlines?${params}`);
25016
+ if (!response.ok) {
25017
+ throw new Error(`Binance markPriceKlines failed: ${response.status}`);
25018
+ }
25019
+ const data = await response.json();
25020
+ return data.map((k) => ({
25021
+ openTime: Number(k[0]),
25022
+ open: parseFloat(k[1]),
25023
+ high: parseFloat(k[2]),
25024
+ low: parseFloat(k[3]),
25025
+ close: parseFloat(k[4]),
25026
+ volume: parseFloat(k[5]),
25027
+ closeTime: Number(k[6])
25028
+ }));
25043
25029
  }
25044
- function useSymmFundingPayments(params) {
25045
- const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25046
- const { address, positionId, limit, offset } = params;
25047
- const chainId = params.chainId ?? ctxChainId;
25048
- const internalEnabled = !!symmCoreClient && !!(address || positionId);
25049
- const request = {
25050
- address,
25051
- positionId,
25052
- chainId,
25053
- limit,
25054
- offset
25030
+ async function fetch24hrTicker(symbol) {
25031
+ const params = new URLSearchParams({ symbol });
25032
+ const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr?${params}`);
25033
+ if (!response.ok) return null;
25034
+ const data = await response.json();
25035
+ return {
25036
+ lastPrice: parseFloat(data.lastPrice),
25037
+ prevClosePrice: parseFloat(data.prevClosePrice),
25038
+ priceChangePercent: parseFloat(data.priceChangePercent)
25055
25039
  };
25056
- return useQuery({
25057
- ...params.query,
25058
- queryKey: symmKeys.fundingPayments({
25059
- address,
25060
- positionId,
25061
- chainId,
25062
- limit,
25063
- offset
25064
- }),
25065
- queryFn: () => symmCoreClient.funding.getPayments(request),
25066
- enabled: internalEnabled && (params.query?.enabled ?? true)
25067
- });
25068
- }
25069
- function useSymmPortfolio(params) {
25070
- const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25071
- const { accountAddress, address } = params;
25072
- const resolvedAddress = accountAddress ? void 0 : address;
25073
- const chainId = params.chainId ?? ctxChainId;
25074
- const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
25075
- return useQuery({
25076
- ...params.query,
25077
- queryKey: symmKeys.portfolio({
25078
- accountAddress,
25079
- address: resolvedAddress,
25080
- chainId
25081
- }),
25082
- queryFn: () => {
25083
- const request = {
25084
- accountAddress,
25085
- address: resolvedAddress,
25086
- chainId
25087
- };
25088
- return symmCoreClient.portfolio.getMetrics(request);
25089
- },
25090
- enabled: internalEnabled && (params.query?.enabled ?? true)
25091
- });
25092
25040
  }
25093
- function useResolvedNotificationsParams(params) {
25094
- const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25095
- const chainId = params.chainId ?? ctxChainId;
25041
+
25042
+ // src/utils/binance-symbol-map.ts
25043
+ var SYMBOL_OVERRIDES = {
25044
+ // Add overrides here as needed for SYMM markets that don't map 1:1 to Binance
25045
+ // e.g., 'SOME_SYMM_SYMBOL': 'BINANCE_SYMBOL',
25046
+ };
25047
+ var UNSUPPORTED_SYMBOLS = /* @__PURE__ */ new Set([
25048
+ // Add symbols here that have no Binance equivalent
25049
+ ]);
25050
+ function resolveBinanceSymbol(symmSymbol) {
25051
+ if (!symmSymbol || !symmSymbol.trim()) {
25052
+ return {
25053
+ symmSymbol,
25054
+ normalizedSymbol: "",
25055
+ binanceSymbol: null,
25056
+ supported: false,
25057
+ reason: "missing_symbol"
25058
+ };
25059
+ }
25060
+ const normalized = symmSymbol.toUpperCase().trim();
25061
+ if (!/^[A-Z0-9]+$/.test(normalized)) {
25062
+ return {
25063
+ symmSymbol,
25064
+ normalizedSymbol: normalized,
25065
+ binanceSymbol: null,
25066
+ supported: false,
25067
+ reason: "invalid_symbol"
25068
+ };
25069
+ }
25070
+ if (UNSUPPORTED_SYMBOLS.has(normalized)) {
25071
+ return {
25072
+ symmSymbol,
25073
+ normalizedSymbol: normalized,
25074
+ binanceSymbol: null,
25075
+ supported: false,
25076
+ reason: "unsupported_symbol"
25077
+ };
25078
+ }
25079
+ const binanceSymbol = SYMBOL_OVERRIDES[normalized] ?? (normalized.endsWith("USDT") ? normalized : `${normalized}USDT`);
25096
25080
  return {
25097
- symmCoreClient,
25098
- chainId,
25099
- userAddress: params.userAddress,
25100
- query: params.query
25081
+ symmSymbol,
25082
+ normalizedSymbol: normalized,
25083
+ binanceSymbol,
25084
+ supported: true,
25085
+ reason: null
25101
25086
  };
25102
25087
  }
25103
- function useSymmNotificationsQuery(params) {
25104
- const { symmCoreClient, chainId, userAddress, query } = useResolvedNotificationsParams(params);
25105
- const internalEnabled = !!symmCoreClient && !!userAddress;
25106
- return useQuery({
25107
- ...query,
25108
- queryKey: symmKeys.notifications(userAddress, chainId),
25109
- queryFn: () => symmCoreClient.notifications.list({
25110
- userAddress,
25111
- chainId
25112
- }),
25113
- enabled: internalEnabled && (query?.enabled ?? true)
25114
- });
25088
+ function getUnsupportedBinanceSymbols(symbols) {
25089
+ return symbols.filter((symbol) => !resolveBinanceSymbol(symbol).supported);
25115
25090
  }
25116
- function useSymmUnreadCountQuery(params) {
25117
- const { symmCoreClient, chainId, userAddress, query } = useResolvedNotificationsParams(params);
25118
- const internalEnabled = !!symmCoreClient && !!userAddress;
25119
- return useQuery({
25120
- ...query,
25121
- queryKey: symmKeys.unreadCount(userAddress, chainId),
25122
- queryFn: () => symmCoreClient.notifications.getUnreadCount({
25123
- userAddress,
25124
- chainId
25125
- }),
25126
- enabled: internalEnabled && (query?.enabled ?? true)
25127
- });
25091
+ function toBinanceSymbol(symmSymbol) {
25092
+ return resolveBinanceSymbol(symmSymbol).binanceSymbol;
25128
25093
  }
25129
- function useSymmMarkReadNotificationMutation(params, options) {
25130
- const { symmCoreClient, chainId, userAddress } = useResolvedNotificationsParams(params);
25131
- const queryClient = useQueryClient();
25132
- return useMutation({
25133
- ...withSymmMutationConfig(options?.mutation, {
25134
- onSuccess: () => {
25135
- queryClient.invalidateQueries({
25136
- queryKey: symmKeys.notifications(userAddress, chainId)
25137
- });
25138
- queryClient.invalidateQueries({
25139
- queryKey: symmKeys.unreadCount(userAddress, chainId)
25140
- });
25141
- }
25142
- }),
25143
- mutationFn: async ({ id, timestamp }) => {
25144
- if (!symmCoreClient || !userAddress) {
25145
- throw new Error("symm-core client not available");
25146
- }
25147
- return symmCoreClient.notifications.markRead({
25148
- id,
25149
- timestamp,
25150
- userAddress,
25151
- chainId
25152
- });
25153
- }
25154
- });
25155
- }
25156
- function useSymmPendingIds(params) {
25157
- const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25158
- const { accountAddress } = params;
25159
- const chainId = params.chainId ?? ctxChainId;
25160
- const internalEnabled = !!symmCoreClient && !!accountAddress;
25161
- return useQuery({
25162
- ...params.query,
25163
- queryKey: symmKeys.pendingIds(accountAddress, chainId),
25164
- queryFn: () => symmCoreClient.positions.getPendingIds({
25165
- address: accountAddress,
25166
- chainId
25167
- }),
25168
- enabled: internalEnabled && (params.query?.enabled ?? true)
25169
- });
25170
- }
25171
- function useSymmPendingInstantOpens(params) {
25172
- const {
25173
- symmCoreClient,
25174
- chainId: ctxChainId
25175
- } = useSymmContext();
25176
- const { accountAddress, authToken: providedAuthToken } = params;
25177
- const chainId = params.chainId ?? ctxChainId;
25178
- const internalEnabled = !!symmCoreClient && !!accountAddress;
25179
- return useQuery({
25180
- ...params.query,
25181
- queryKey: symmKeys.pendingInstantOpens(accountAddress, chainId),
25182
- queryFn: async () => {
25183
- const authToken = providedAuthToken ?? useSymmAuthStore.getState().getToken(accountAddress, chainId);
25184
- if (!authToken) {
25185
- throw new Error("failed to acquire auth token for pending instant opens");
25186
- }
25187
- return symmCoreClient.positions.getPendingInstantOpens({
25188
- accountAddress,
25189
- chainId,
25190
- authToken
25191
- });
25192
- },
25193
- enabled: internalEnabled && (params.query?.enabled ?? true)
25194
- });
25195
- }
25196
- function useSymmTwapOrder(params) {
25197
- const { symmCoreClient } = useSymmContext();
25198
- const { orderId } = params;
25199
- const internalEnabled = !!symmCoreClient && !!orderId;
25200
- return useQuery({
25201
- ...params.query,
25202
- queryKey: symmKeys.twapOrder(orderId),
25203
- queryFn: () => symmCoreClient.orders.getTwapOrder(orderId),
25204
- enabled: internalEnabled && (params.query?.enabled ?? true)
25205
- });
25206
- }
25207
- var useSymmWsStore = create((set) => ({
25208
- isConnected: false,
25209
- setConnected: (isConnected) => set({ isConnected })
25210
- }));
25211
-
25212
- // src/react/hooks/use-symm-ws.ts
25213
- function asUnsubscribeFn(value) {
25214
- return typeof value === "function" ? value : null;
25215
- }
25216
- function useSymmWs(params = {}) {
25217
- const {
25218
- symmCoreClient: ctxClient,
25219
- address: ctxAddress,
25220
- chainId: ctxChainId
25221
- } = useSymmContext();
25222
- const queryClient = useQueryClient();
25223
- const isConnected = useSymmWsStore((state) => state.isConnected);
25224
- const setConnected = useSymmWsStore((state) => state.setConnected);
25225
- const symmCoreClient = params.symmCoreClient ?? ctxClient;
25226
- const accountAddress = params.accountAddress ?? ctxAddress;
25227
- const chainId = params.chainId ?? ctxChainId;
25228
- useEffect(() => {
25229
- if (!symmCoreClient || !accountAddress) {
25230
- setConnected(false);
25231
- return;
25232
- }
25233
- const ws = symmCoreClient.ws;
25234
- const addr = accountAddress;
25235
- const unsubscribers = [];
25236
- const removeOnConnect = ws.onConnect(() => setConnected(true));
25237
- const removeOnDisconnect = ws.onDisconnect(() => setConnected(false));
25238
- unsubscribers.push(removeOnConnect, removeOnDisconnect);
25239
- const positionsUnsub = asUnsubscribeFn(
25240
- ws.subscribeToPositions(addr, chainId, () => {
25241
- queryClient.invalidateQueries({
25242
- queryKey: ["symm", "positions"]
25243
- });
25244
- })
25245
- );
25246
- if (positionsUnsub) unsubscribers.push(positionsUnsub);
25247
- const openOrdersUnsub = asUnsubscribeFn(
25248
- ws.subscribeToOpenOrders(addr, chainId, () => {
25249
- queryClient.invalidateQueries({
25250
- queryKey: ["symm", "openOrders"]
25251
- });
25252
- })
25253
- );
25254
- if (openOrdersUnsub) unsubscribers.push(openOrdersUnsub);
25255
- const tradesUnsub = asUnsubscribeFn(
25256
- ws.subscribeToTrades(addr, chainId, () => {
25257
- queryClient.invalidateQueries({
25258
- queryKey: ["symm", "tradeHistory"]
25259
- });
25260
- })
25261
- );
25262
- if (tradesUnsub) unsubscribers.push(tradesUnsub);
25263
- const accountSummaryUnsub = asUnsubscribeFn(
25264
- ws.subscribeToAccountSummary(addr, chainId, () => {
25265
- queryClient.invalidateQueries({
25266
- queryKey: symmKeys.balances(accountAddress, chainId)
25267
- });
25268
- queryClient.invalidateQueries({
25269
- queryKey: symmKeys.accountSummary(accountAddress, chainId)
25270
- });
25271
- })
25272
- );
25273
- if (accountSummaryUnsub) unsubscribers.push(accountSummaryUnsub);
25274
- const notificationsUnsub = asUnsubscribeFn(
25275
- ws.subscribeToNotifications(addr, chainId, () => {
25276
- queryClient.invalidateQueries({
25277
- queryKey: symmKeys.notifications(accountAddress, chainId)
25278
- });
25279
- queryClient.invalidateQueries({
25280
- queryKey: symmKeys.unreadCount(accountAddress, chainId)
25281
- });
25282
- })
25283
- );
25284
- if (notificationsUnsub) unsubscribers.push(notificationsUnsub);
25285
- const tpslUnsub = asUnsubscribeFn(
25286
- ws.subscribeToTpsl(addr, chainId, () => {
25287
- queryClient.invalidateQueries({ queryKey: ["symm", "tpslOrders"] });
25288
- queryClient.invalidateQueries({ queryKey: ["symm", "openOrders"] });
25289
- })
25290
- );
25291
- if (tpslUnsub) unsubscribers.push(tpslUnsub);
25292
- const twapUnsub = asUnsubscribeFn(
25293
- ws.subscribeToTwapOrders(addr, chainId, () => {
25294
- queryClient.invalidateQueries({ queryKey: ["symm", "twapOrders"] });
25295
- queryClient.invalidateQueries({ queryKey: ["symm", "openOrders"] });
25296
- })
25297
- );
25298
- if (twapUnsub) unsubscribers.push(twapUnsub);
25299
- const triggerOrdersUnsub = asUnsubscribeFn(
25300
- ws.subscribeToTriggerOrders(addr, chainId, () => {
25301
- queryClient.invalidateQueries({ queryKey: ["symm", "triggerOrders"] });
25302
- queryClient.invalidateQueries({ queryKey: ["symm", "openOrders"] });
25303
- })
25304
- );
25305
- if (triggerOrdersUnsub) unsubscribers.push(triggerOrdersUnsub);
25306
- const executionsUnsub = asUnsubscribeFn(
25307
- ws.subscribeToExecutions(addr, chainId, () => {
25308
- queryClient.invalidateQueries({
25309
- queryKey: ["symm", "positions"]
25310
- });
25311
- queryClient.invalidateQueries({
25312
- queryKey: ["symm", "portfolio"]
25313
- });
25314
- })
25315
- );
25316
- if (executionsUnsub) unsubscribers.push(executionsUnsub);
25317
- return () => {
25318
- unsubscribers.forEach((unsubscribe) => unsubscribe());
25319
- };
25320
- }, [symmCoreClient, accountAddress, chainId, queryClient, setConnected]);
25321
- return { isConnected };
25322
- }
25323
- var STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USD", "USDT", "USDE", "USDH", "USDT0"]);
25324
- function useSymmChartSelection(input) {
25325
- const {
25326
- longSymbol,
25327
- shortSymbol,
25328
- longWeight = 100,
25329
- shortWeight = 100,
25330
- longTokens: explicitLongTokens,
25331
- shortTokens: explicitShortTokens
25332
- } = input;
25333
- return useMemo(() => {
25334
- const longTokens = explicitLongTokens?.length ? explicitLongTokens : longSymbol ? [{ symbol: longSymbol, weight: longWeight }] : [];
25335
- const shortTokens = explicitShortTokens?.length ? explicitShortTokens : shortSymbol ? [{ symbol: shortSymbol, weight: shortWeight }] : [];
25336
- const selectedSymbols = [
25337
- ...longTokens.map((t) => t.symbol),
25338
- ...shortTokens.map((t) => t.symbol)
25339
- ];
25340
- const uniqueSelectedSymbols = Array.from(new Set(selectedSymbols));
25341
- const longPrimarySymbol = longTokens[0]?.symbol ?? null;
25342
- const shortPrimarySymbol = shortTokens[0]?.symbol ?? null;
25343
- const longIsStable = longPrimarySymbol ? STABLE_SYMBOLS.has(longPrimarySymbol.toUpperCase()) : true;
25344
- const shortIsStable = shortPrimarySymbol ? STABLE_SYMBOLS.has(shortPrimarySymbol.toUpperCase()) : true;
25345
- let isSingleSided = false;
25346
- let activeSide = null;
25347
- let activeTokenSymbol;
25348
- if (longTokens.length > 0 && !longIsStable && (shortTokens.length === 0 || shortIsStable)) {
25349
- isSingleSided = true;
25350
- activeSide = "long";
25351
- activeTokenSymbol = longPrimarySymbol ?? void 0;
25352
- } else if (shortTokens.length > 0 && !shortIsStable && (longTokens.length === 0 || longIsStable)) {
25353
- isSingleSided = true;
25354
- activeSide = "short";
25355
- activeTokenSymbol = shortPrimarySymbol ?? void 0;
25356
- }
25357
- const longLabel = longTokens.map((token) => token.symbol).join("+") || "USDC";
25358
- const shortLabel = shortTokens.map((token) => token.symbol).join("+") || "USDC";
25359
- const pairLabel = `${longLabel}/${shortLabel}`;
25360
- return {
25361
- longTokens,
25362
- shortTokens,
25363
- isSingleSided,
25364
- activeSide,
25365
- activeTokenSymbol,
25366
- pairLabel,
25367
- selectedSymbols: uniqueSelectedSymbols
25368
- };
25369
- }, [
25370
- explicitLongTokens,
25371
- explicitShortTokens,
25372
- longSymbol,
25373
- shortSymbol,
25374
- longWeight,
25375
- shortWeight
25376
- ]);
25377
- }
25378
-
25379
- // src/utils/binance-symbol-map.ts
25380
- var SYMBOL_OVERRIDES = {
25381
- // Add overrides here as needed for SYMM markets that don't map 1:1 to Binance
25382
- // e.g., 'SOME_SYMM_SYMBOL': 'BINANCE_SYMBOL',
25383
- };
25384
- var UNSUPPORTED_SYMBOLS = /* @__PURE__ */ new Set([
25385
- // Add symbols here that have no Binance equivalent
25386
- ]);
25387
- function resolveBinanceSymbol(symmSymbol) {
25388
- if (!symmSymbol || !symmSymbol.trim()) {
25389
- return {
25390
- symmSymbol,
25391
- normalizedSymbol: "",
25392
- binanceSymbol: null,
25393
- supported: false,
25394
- reason: "missing_symbol"
25395
- };
25396
- }
25397
- const normalized = symmSymbol.toUpperCase().trim();
25398
- if (!/^[A-Z0-9]+$/.test(normalized)) {
25399
- return {
25400
- symmSymbol,
25401
- normalizedSymbol: normalized,
25402
- binanceSymbol: null,
25403
- supported: false,
25404
- reason: "invalid_symbol"
25405
- };
25406
- }
25407
- if (UNSUPPORTED_SYMBOLS.has(normalized)) {
25408
- return {
25409
- symmSymbol,
25410
- normalizedSymbol: normalized,
25411
- binanceSymbol: null,
25412
- supported: false,
25413
- reason: "unsupported_symbol"
25414
- };
25415
- }
25416
- const binanceSymbol = SYMBOL_OVERRIDES[normalized] ?? (normalized.endsWith("USDT") ? normalized : `${normalized}USDT`);
25417
- return {
25418
- symmSymbol,
25419
- normalizedSymbol: normalized,
25420
- binanceSymbol,
25421
- supported: true,
25422
- reason: null
25423
- };
25424
- }
25425
- function getUnsupportedBinanceSymbols(symbols) {
25426
- return symbols.filter((symbol) => !resolveBinanceSymbol(symbol).supported);
25427
- }
25428
- function toBinanceSymbol(symmSymbol) {
25429
- return resolveBinanceSymbol(symmSymbol).binanceSymbol;
25430
- }
25431
-
25432
- // src/utils/binance-api.ts
25433
- var BINANCE_FAPI_BASE = "https://fapi.binance.com";
25434
- async function fetchMarkPriceKlines(symbol, interval, startTime, endTime, limit = 1500) {
25435
- const params = new URLSearchParams({
25436
- symbol,
25437
- interval,
25438
- startTime: String(startTime),
25439
- endTime: String(endTime),
25440
- limit: String(Math.min(limit, 1500))
25441
- });
25442
- const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/markPriceKlines?${params}`);
25443
- if (!response.ok) {
25444
- throw new Error(`Binance markPriceKlines failed: ${response.status}`);
25445
- }
25446
- const data = await response.json();
25447
- return data.map((k) => ({
25448
- openTime: Number(k[0]),
25449
- open: parseFloat(k[1]),
25450
- high: parseFloat(k[2]),
25451
- low: parseFloat(k[3]),
25452
- close: parseFloat(k[4]),
25453
- volume: parseFloat(k[5]),
25454
- closeTime: Number(k[6])
25455
- }));
25456
- }
25457
- async function fetch24hrTicker(symbol) {
25458
- const params = new URLSearchParams({ symbol });
25459
- const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr?${params}`);
25460
- if (!response.ok) return null;
25461
- const data = await response.json();
25462
- return {
25463
- lastPrice: parseFloat(data.lastPrice),
25464
- prevClosePrice: parseFloat(data.prevClosePrice),
25465
- priceChangePercent: parseFloat(data.priceChangePercent)
25466
- };
25467
- }
25468
-
25469
- // src/utils/chart-metrics.ts
25470
- function getPositiveValue(metadata, field) {
25471
- const value = metadata?.[field];
25472
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
25473
- return null;
25474
- }
25475
- return value;
25476
- }
25477
- function computeWeightedProduct(tokens, metadataMap, field, invert = false) {
25478
- let product = 1;
25479
- let hasToken = false;
25480
- for (const token of tokens) {
25481
- if (!(token.weight > 0)) {
25482
- continue;
25483
- }
25484
- const price = getPositiveValue(metadataMap[token.symbol], field);
25485
- if (price === null) {
25486
- return null;
25487
- }
25488
- hasToken = true;
25489
- const exponent = invert ? -(token.weight / 100) : token.weight / 100;
25490
- product *= Math.pow(price, exponent);
25491
- }
25492
- return hasToken ? product : 1;
25493
- }
25494
- function computePriceRatio({
25495
- longTokens,
25496
- shortTokens,
25497
- longTokensMetadata,
25498
- shortTokensMetadata
25499
- }) {
25500
- const firstLong = longTokens[0];
25501
- const firstShort = shortTokens[0];
25502
- const longPrice = firstLong ? getPositiveValue(longTokensMetadata[firstLong.symbol], "currentPrice") : null;
25503
- const shortPrice = firstShort ? getPositiveValue(shortTokensMetadata[firstShort.symbol], "currentPrice") : null;
25504
- if (longPrice !== null && shortPrice !== null) {
25505
- return longPrice / shortPrice;
25506
- }
25507
- if (longPrice !== null && shortTokens.length === 0) {
25508
- return longPrice;
25509
- }
25510
- if (shortPrice !== null && longTokens.length === 0) {
25511
- return shortPrice;
25512
- }
25513
- return null;
25514
- }
25515
- function computePriceRatio24h({
25516
- longTokens,
25517
- shortTokens,
25518
- longTokensMetadata,
25519
- shortTokensMetadata
25520
- }) {
25521
- const firstLong = longTokens[0];
25522
- const firstShort = shortTokens[0];
25523
- const longPrice = firstLong ? getPositiveValue(longTokensMetadata[firstLong.symbol], "prevDayPrice") : null;
25524
- const shortPrice = firstShort ? getPositiveValue(shortTokensMetadata[firstShort.symbol], "prevDayPrice") : null;
25525
- if (longPrice !== null && shortPrice !== null) {
25526
- return longPrice / shortPrice;
25527
- }
25528
- if (longPrice !== null && shortTokens.length === 0) {
25529
- return longPrice;
25530
- }
25531
- if (shortPrice !== null && longTokens.length === 0) {
25532
- return shortPrice;
25533
- }
25534
- return null;
25535
- }
25536
- function computeWeightedRatio({
25537
- longTokens,
25538
- shortTokens,
25539
- longTokensMetadata,
25540
- shortTokensMetadata
25541
- }) {
25542
- const longProduct = computeWeightedProduct(
25543
- longTokens,
25544
- longTokensMetadata,
25545
- "currentPrice"
25546
- );
25547
- const shortProduct = computeWeightedProduct(
25548
- shortTokens,
25549
- shortTokensMetadata,
25550
- "currentPrice",
25551
- true
25552
- );
25553
- if (longProduct === null || shortProduct === null) {
25554
- return null;
25555
- }
25556
- return longProduct * shortProduct;
25557
- }
25558
- function computeWeightedRatio24h({
25559
- longTokens,
25560
- shortTokens,
25561
- longTokensMetadata,
25562
- shortTokensMetadata
25563
- }) {
25564
- const longProduct = computeWeightedProduct(
25565
- longTokens,
25566
- longTokensMetadata,
25567
- "prevDayPrice"
25568
- );
25569
- const shortProduct = computeWeightedProduct(
25570
- shortTokens,
25571
- shortTokensMetadata,
25572
- "prevDayPrice",
25573
- true
25574
- );
25575
- if (longProduct === null || shortProduct === null) {
25576
- return null;
25577
- }
25578
- return longProduct * shortProduct;
25579
- }
25580
- function computeNetFundingSum({
25581
- longTokens,
25582
- shortTokens,
25583
- longTokensMetadata,
25584
- shortTokensMetadata
25585
- }) {
25586
- let funding = 0;
25587
- for (const token of longTokens) {
25588
- const value = longTokensMetadata[token.symbol]?.netFunding;
25589
- if (typeof value === "number" && Number.isFinite(value)) {
25590
- funding += value;
25591
- }
25592
- }
25593
- for (const token of shortTokens) {
25594
- const value = shortTokensMetadata[token.symbol]?.netFunding;
25595
- if (typeof value === "number" && Number.isFinite(value)) {
25596
- funding += value;
25597
- }
25598
- }
25599
- return funding;
25600
- }
25601
-
25602
- // src/utils/binance-ws.ts
25603
- var BINANCE_WS_URL = "wss://fstream.binance.com/ws";
25604
- var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
25605
- var BinanceWsManager = class {
25606
- ws = null;
25607
- streams = /* @__PURE__ */ new Map();
25608
- reconnectAttempt = 0;
25609
- reconnectTimer = null;
25610
- intentionalClose = false;
25611
- pendingSubscribes = [];
25612
- idCounter = 0;
25613
- /**
25614
- * Subscribe to a kline stream. Returns an unsubscribe function.
25615
- */
25616
- subscribeKline(symbol, interval, cb) {
25617
- const streamName = `${symbol.toLowerCase()}@kline_${interval}`;
25618
- const id = this.generateId();
25619
- const wrappedCb = (raw) => {
25620
- const k = raw.k;
25621
- if (!k) return;
25622
- cb({
25623
- symbol: raw.s,
25624
- interval: k.i,
25625
- openTime: k.t,
25626
- closeTime: k.T,
25627
- open: parseFloat(k.o),
25628
- high: parseFloat(k.h),
25629
- low: parseFloat(k.l),
25630
- close: parseFloat(k.c),
25631
- volume: parseFloat(k.v),
25632
- isFinal: k.x
25094
+
25095
+ // src/utils/binance-ws.ts
25096
+ var BINANCE_WS_URL = "wss://fstream.binance.com/ws";
25097
+ var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
25098
+ var BinanceWsManager = class {
25099
+ ws = null;
25100
+ streams = /* @__PURE__ */ new Map();
25101
+ reconnectAttempt = 0;
25102
+ reconnectTimer = null;
25103
+ intentionalClose = false;
25104
+ pendingSubscribes = [];
25105
+ idCounter = 0;
25106
+ /**
25107
+ * Subscribe to a kline stream. Returns an unsubscribe function.
25108
+ */
25109
+ subscribeKline(symbol, interval, cb) {
25110
+ const streamName = `${symbol.toLowerCase()}@kline_${interval}`;
25111
+ const id = this.generateId();
25112
+ const wrappedCb = (raw) => {
25113
+ const k = raw.k;
25114
+ if (!k) return;
25115
+ cb({
25116
+ symbol: raw.s,
25117
+ interval: k.i,
25118
+ openTime: k.t,
25119
+ closeTime: k.T,
25120
+ open: parseFloat(k.o),
25121
+ high: parseFloat(k.h),
25122
+ low: parseFloat(k.l),
25123
+ close: parseFloat(k.c),
25124
+ volume: parseFloat(k.v),
25125
+ isFinal: k.x
25633
25126
  });
25634
25127
  };
25635
25128
  this.addStreamCallback(streamName, id, wrappedCb);
@@ -25648,225 +25141,837 @@ var BinanceWsManager = class {
25648
25141
  indexPrice: parseFloat(raw.i),
25649
25142
  time: raw.E
25650
25143
  });
25651
- };
25652
- this.addStreamCallback(streamName, id, wrappedCb);
25653
- return () => this.removeStreamCallback(streamName, id);
25654
- }
25655
- /**
25656
- * Destroy the manager and close the connection.
25657
- */
25658
- destroy() {
25659
- this.intentionalClose = true;
25660
- if (this.reconnectTimer) {
25661
- clearTimeout(this.reconnectTimer);
25662
- this.reconnectTimer = null;
25663
- }
25664
- if (this.ws) {
25665
- this.ws.close();
25666
- this.ws = null;
25144
+ };
25145
+ this.addStreamCallback(streamName, id, wrappedCb);
25146
+ return () => this.removeStreamCallback(streamName, id);
25147
+ }
25148
+ /**
25149
+ * Destroy the manager and close the connection.
25150
+ */
25151
+ destroy() {
25152
+ this.intentionalClose = true;
25153
+ if (this.reconnectTimer) {
25154
+ clearTimeout(this.reconnectTimer);
25155
+ this.reconnectTimer = null;
25156
+ }
25157
+ if (this.ws) {
25158
+ this.ws.close();
25159
+ this.ws = null;
25160
+ }
25161
+ this.streams.clear();
25162
+ }
25163
+ // --- Private ---
25164
+ generateId() {
25165
+ return `sub_${++this.idCounter}_${Date.now()}`;
25166
+ }
25167
+ addStreamCallback(streamName, id, cb) {
25168
+ let sub = this.streams.get(streamName);
25169
+ const isNew = !sub;
25170
+ if (!sub) {
25171
+ sub = { callbacks: /* @__PURE__ */ new Map() };
25172
+ this.streams.set(streamName, sub);
25173
+ }
25174
+ sub.callbacks.set(id, cb);
25175
+ if (isNew) {
25176
+ this.ensureConnected();
25177
+ this.sendSubscribe([streamName]);
25178
+ }
25179
+ }
25180
+ removeStreamCallback(streamName, id) {
25181
+ const sub = this.streams.get(streamName);
25182
+ if (!sub) return;
25183
+ sub.callbacks.delete(id);
25184
+ if (sub.callbacks.size === 0) {
25185
+ this.streams.delete(streamName);
25186
+ this.sendUnsubscribe([streamName]);
25187
+ if (this.streams.size === 0 && this.ws) {
25188
+ this.intentionalClose = true;
25189
+ this.ws.close();
25190
+ this.ws = null;
25191
+ this.intentionalClose = false;
25192
+ }
25193
+ }
25194
+ }
25195
+ ensureConnected() {
25196
+ if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
25197
+ return;
25198
+ }
25199
+ this.connect();
25200
+ }
25201
+ connect() {
25202
+ if (typeof WebSocket === "undefined") return;
25203
+ this.intentionalClose = false;
25204
+ this.ws = new WebSocket(BINANCE_WS_URL);
25205
+ this.ws.onopen = () => {
25206
+ this.reconnectAttempt = 0;
25207
+ const activeStreams = Array.from(this.streams.keys());
25208
+ if (activeStreams.length > 0) {
25209
+ this.sendSubscribe(activeStreams);
25210
+ }
25211
+ if (this.pendingSubscribes.length > 0) {
25212
+ this.sendSubscribe(this.pendingSubscribes);
25213
+ this.pendingSubscribes = [];
25214
+ }
25215
+ };
25216
+ this.ws.onmessage = (event) => {
25217
+ try {
25218
+ const data = JSON.parse(event.data);
25219
+ this.handleMessage(data);
25220
+ } catch {
25221
+ }
25222
+ };
25223
+ this.ws.onclose = () => {
25224
+ if (this.intentionalClose) return;
25225
+ this.scheduleReconnect();
25226
+ };
25227
+ this.ws.onerror = () => {
25228
+ };
25229
+ }
25230
+ handleMessage(data) {
25231
+ if (data.e === "kline") {
25232
+ const k = data.k;
25233
+ const streamName = `${data.s.toLowerCase()}@kline_${k.i}`;
25234
+ this.dispatchToStream(streamName, data);
25235
+ } else if (data.e === "markPriceUpdate") {
25236
+ const streamName = `${data.s.toLowerCase()}@markPrice@1s`;
25237
+ this.dispatchToStream(streamName, data);
25238
+ }
25239
+ }
25240
+ dispatchToStream(streamName, data) {
25241
+ const sub = this.streams.get(streamName);
25242
+ if (!sub) return;
25243
+ sub.callbacks.forEach((cb) => {
25244
+ try {
25245
+ cb(data);
25246
+ } catch {
25247
+ }
25248
+ });
25249
+ }
25250
+ sendSubscribe(streams) {
25251
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
25252
+ this.pendingSubscribes.push(...streams);
25253
+ return;
25254
+ }
25255
+ this.ws.send(JSON.stringify({
25256
+ method: "SUBSCRIBE",
25257
+ params: streams,
25258
+ id: Date.now()
25259
+ }));
25260
+ }
25261
+ sendUnsubscribe(streams) {
25262
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
25263
+ this.pendingSubscribes = this.pendingSubscribes.filter(
25264
+ (s) => !streams.includes(s)
25265
+ );
25266
+ return;
25267
+ }
25268
+ this.ws.send(JSON.stringify({
25269
+ method: "UNSUBSCRIBE",
25270
+ params: streams,
25271
+ id: Date.now()
25272
+ }));
25273
+ }
25274
+ scheduleReconnect() {
25275
+ if (this.reconnectTimer) return;
25276
+ if (this.streams.size === 0) return;
25277
+ const delay = RECONNECT_DELAYS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS.length - 1)];
25278
+ this.reconnectAttempt++;
25279
+ this.reconnectTimer = setTimeout(() => {
25280
+ this.reconnectTimer = null;
25281
+ this.connect();
25282
+ }, delay);
25283
+ }
25284
+ };
25285
+ var _instance = null;
25286
+ function getBinanceWsManager() {
25287
+ if (!_instance) {
25288
+ _instance = new BinanceWsManager();
25289
+ }
25290
+ return _instance;
25291
+ }
25292
+
25293
+ // src/react/stores/use-binance-mark-price-store.ts
25294
+ var refCounts = /* @__PURE__ */ new Map();
25295
+ var streamUnsubs = /* @__PURE__ */ new Map();
25296
+ var streamSymbols = /* @__PURE__ */ new Map();
25297
+ function normalizeBinanceSymbol(symbol) {
25298
+ return symbol.toUpperCase().trim();
25299
+ }
25300
+ function getNextRefCount(binanceSymbol) {
25301
+ return (refCounts.get(binanceSymbol) ?? 0) + 1;
25302
+ }
25303
+ function getPrevRefCount(binanceSymbol) {
25304
+ return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);
25305
+ }
25306
+ var useBinanceMarkPriceStore = create((set) => ({
25307
+ markPrices: {},
25308
+ subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
25309
+ const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
25310
+ const nextRefCount = getNextRefCount(binanceSymbol);
25311
+ refCounts.set(binanceSymbol, nextRefCount);
25312
+ const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
25313
+ symbols.add(symmSymbol);
25314
+ streamSymbols.set(binanceSymbol, symbols);
25315
+ if (nextRefCount === 1) {
25316
+ const wsManager = getBinanceWsManager();
25317
+ const unsubscribe = wsManager.subscribeMarkPrice(binanceSymbol, (data) => {
25318
+ const canonicalSymbol = normalizeBinanceSymbol(data.symbol);
25319
+ const mappedSymbols = streamSymbols.get(canonicalSymbol);
25320
+ if (!mappedSymbols || mappedSymbols.size === 0) return;
25321
+ set((state) => {
25322
+ const nextMarkPrices = { ...state.markPrices };
25323
+ mappedSymbols.forEach((mappedSymbol) => {
25324
+ nextMarkPrices[mappedSymbol] = data.markPrice;
25325
+ });
25326
+ return { markPrices: nextMarkPrices };
25327
+ });
25328
+ });
25329
+ streamUnsubs.set(binanceSymbol, unsubscribe);
25667
25330
  }
25668
- this.streams.clear();
25669
- }
25670
- // --- Private ---
25671
- generateId() {
25672
- return `sub_${++this.idCounter}_${Date.now()}`;
25673
- }
25674
- addStreamCallback(streamName, id, cb) {
25675
- let sub = this.streams.get(streamName);
25676
- const isNew = !sub;
25677
- if (!sub) {
25678
- sub = { callbacks: /* @__PURE__ */ new Map() };
25679
- this.streams.set(streamName, sub);
25331
+ },
25332
+ unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
25333
+ const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
25334
+ const symbols = streamSymbols.get(binanceSymbol);
25335
+ if (symbols) {
25336
+ symbols.delete(symmSymbol);
25337
+ if (symbols.size === 0) {
25338
+ streamSymbols.delete(binanceSymbol);
25339
+ } else {
25340
+ streamSymbols.set(binanceSymbol, symbols);
25341
+ }
25680
25342
  }
25681
- sub.callbacks.set(id, cb);
25682
- if (isNew) {
25683
- this.ensureConnected();
25684
- this.sendSubscribe([streamName]);
25343
+ const nextRefCount = getPrevRefCount(binanceSymbol);
25344
+ if (nextRefCount === 0) {
25345
+ const unsubscribe = streamUnsubs.get(binanceSymbol);
25346
+ if (unsubscribe) unsubscribe();
25347
+ streamUnsubs.delete(binanceSymbol);
25348
+ refCounts.delete(binanceSymbol);
25349
+ } else {
25350
+ refCounts.set(binanceSymbol, nextRefCount);
25685
25351
  }
25352
+ set((state) => {
25353
+ if (state.markPrices[symmSymbol] == null) return state;
25354
+ const nextMarkPrices = { ...state.markPrices };
25355
+ delete nextMarkPrices[symmSymbol];
25356
+ return { markPrices: nextMarkPrices };
25357
+ });
25686
25358
  }
25687
- removeStreamCallback(streamName, id) {
25688
- const sub = this.streams.get(streamName);
25689
- if (!sub) return;
25690
- sub.callbacks.delete(id);
25691
- if (sub.callbacks.size === 0) {
25692
- this.streams.delete(streamName);
25693
- this.sendUnsubscribe([streamName]);
25694
- if (this.streams.size === 0 && this.ws) {
25695
- this.intentionalClose = true;
25696
- this.ws.close();
25697
- this.ws = null;
25698
- this.intentionalClose = false;
25359
+ }));
25360
+
25361
+ // src/react/hooks/use-symm-token-selection-markets.ts
25362
+ async function fetchTickerSnapshot(symbol) {
25363
+ const resolution = resolveBinanceSymbol(symbol);
25364
+ if (!resolution.binanceSymbol) return null;
25365
+ const ticker = await fetch24hrTicker(resolution.binanceSymbol);
25366
+ if (!ticker) return null;
25367
+ return {
25368
+ prevClosePrice: ticker.prevClosePrice
25369
+ };
25370
+ }
25371
+ function useSymmTokenSelectionMarkets(params) {
25372
+ const query = useSymmHedgerMarkets(params);
25373
+ const baseMarkets = query.data?.filteredMarkets ?? query.data?.markets ?? [];
25374
+ const marketSymbols = useMemo(
25375
+ () => Array.from(
25376
+ new Set(
25377
+ baseMarkets.map((market) => market.symbol).filter((symbol) => !!symbol)
25378
+ )
25379
+ ),
25380
+ [baseMarkets]
25381
+ );
25382
+ const symbolsKey = useMemo(
25383
+ () => [...marketSymbols].sort().join(","),
25384
+ [marketSymbols]
25385
+ );
25386
+ const symbolToBinanceMap = useMemo(
25387
+ () => new Map(
25388
+ marketSymbols.map((symbol) => {
25389
+ const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;
25390
+ if (!binanceSymbol) return null;
25391
+ return [symbol, binanceSymbol];
25392
+ }).filter((entry) => !!entry)
25393
+ ),
25394
+ [marketSymbols]
25395
+ );
25396
+ const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
25397
+ const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
25398
+ const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
25399
+ const priceQuery = useQuery({
25400
+ queryKey: ["symm", "token-selection-markets-price", symbolsKey],
25401
+ queryFn: async () => {
25402
+ const tickerSnapshots = {};
25403
+ await Promise.all(
25404
+ marketSymbols.map(async (symbol) => {
25405
+ tickerSnapshots[symbol] = await fetchTickerSnapshot(symbol);
25406
+ })
25407
+ );
25408
+ return { tickerSnapshots };
25409
+ },
25410
+ enabled: query.isSuccess && marketSymbols.length > 0,
25411
+ staleTime: 6e4,
25412
+ gcTime: 5 * 6e4
25413
+ });
25414
+ useEffect(() => {
25415
+ if (!query.isSuccess || symbolToBinanceMap.size === 0) {
25416
+ return;
25417
+ }
25418
+ const symbolEntries = Array.from(symbolToBinanceMap.entries());
25419
+ symbolEntries.forEach(
25420
+ ([symbol, binanceSymbol]) => subscribeSymbol(symbol, binanceSymbol)
25421
+ );
25422
+ return () => {
25423
+ symbolEntries.forEach(
25424
+ ([symbol, binanceSymbol]) => unsubscribeSymbol(symbol, binanceSymbol)
25425
+ );
25426
+ };
25427
+ }, [query.isSuccess, symbolsKey, symbolToBinanceMap, subscribeSymbol, unsubscribeSymbol]);
25428
+ const markets = useMemo(() => {
25429
+ const snapshots = priceQuery.data?.tickerSnapshots ?? {};
25430
+ return baseMarkets.map((market) => {
25431
+ const symbol = market.symbol ?? "";
25432
+ const markPrice = symbol ? liveMarkPrices[symbol] ?? null : null;
25433
+ const prevDayPrice = symbol ? snapshots[symbol]?.prevClosePrice ?? null : null;
25434
+ const priceChange24h = markPrice != null && prevDayPrice != null ? markPrice - prevDayPrice : null;
25435
+ const priceChange24hPercent = markPrice != null && prevDayPrice != null && prevDayPrice !== 0 ? (markPrice - prevDayPrice) / prevDayPrice * 100 : null;
25436
+ return {
25437
+ ...market,
25438
+ collateralToken: "USDC",
25439
+ markPrice,
25440
+ prevDayPrice,
25441
+ priceChange24h,
25442
+ priceChange24hPercent
25443
+ };
25444
+ });
25445
+ }, [baseMarkets, liveMarkPrices, priceQuery.data]);
25446
+ const marketsBySymbol = useMemo(
25447
+ () => new Map(
25448
+ markets.filter((m) => !!m.symbol).map((m) => [m.symbol, m])
25449
+ ),
25450
+ [markets]
25451
+ );
25452
+ const marketsById = useMemo(
25453
+ () => new Map(markets.map((market) => [market.id, market])),
25454
+ [markets]
25455
+ );
25456
+ return {
25457
+ query,
25458
+ priceQuery,
25459
+ markets,
25460
+ marketsBySymbol,
25461
+ marketsById,
25462
+ isLoading: query.isLoading || priceQuery.isLoading,
25463
+ isPriceLoading: priceQuery.isLoading
25464
+ };
25465
+ }
25466
+ function useSymmFunding(params) {
25467
+ const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25468
+ const chainId = params?.chainId ?? ctxChainId;
25469
+ const internalEnabled = !!symmCoreClient;
25470
+ return useQuery({
25471
+ ...params?.query,
25472
+ queryKey: symmKeys.fundingRates(chainId),
25473
+ queryFn: () => symmCoreClient.funding.getRates({ chainId }),
25474
+ enabled: internalEnabled && (params?.query?.enabled ?? true)
25475
+ });
25476
+ }
25477
+ function useSymmFundingHistory(params) {
25478
+ const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25479
+ const chainId = params.chainId ?? ctxChainId;
25480
+ const {
25481
+ period,
25482
+ longSymbols,
25483
+ shortSymbols,
25484
+ interval,
25485
+ weightMode,
25486
+ includeContributions
25487
+ } = params;
25488
+ const internalEnabled = !!symmCoreClient;
25489
+ const request = {
25490
+ chainId,
25491
+ period,
25492
+ longSymbols,
25493
+ shortSymbols,
25494
+ interval,
25495
+ weightMode,
25496
+ includeContributions
25497
+ };
25498
+ return useQuery({
25499
+ ...params.query,
25500
+ queryKey: symmKeys.fundingHistory(request),
25501
+ queryFn: () => symmCoreClient.funding.getHistory(request),
25502
+ enabled: internalEnabled && (params.query?.enabled ?? true)
25503
+ });
25504
+ }
25505
+ function useSymmFundingPayments(params) {
25506
+ const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25507
+ const { address, positionId, limit, offset } = params;
25508
+ const chainId = params.chainId ?? ctxChainId;
25509
+ const internalEnabled = !!symmCoreClient && !!(address || positionId);
25510
+ const request = {
25511
+ address,
25512
+ positionId,
25513
+ chainId,
25514
+ limit,
25515
+ offset
25516
+ };
25517
+ return useQuery({
25518
+ ...params.query,
25519
+ queryKey: symmKeys.fundingPayments({
25520
+ address,
25521
+ positionId,
25522
+ chainId,
25523
+ limit,
25524
+ offset
25525
+ }),
25526
+ queryFn: () => symmCoreClient.funding.getPayments(request),
25527
+ enabled: internalEnabled && (params.query?.enabled ?? true)
25528
+ });
25529
+ }
25530
+ function useSymmPortfolio(params) {
25531
+ const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25532
+ const { accountAddress, address } = params;
25533
+ const resolvedAddress = accountAddress ? void 0 : address;
25534
+ const chainId = params.chainId ?? ctxChainId;
25535
+ const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
25536
+ return useQuery({
25537
+ ...params.query,
25538
+ queryKey: symmKeys.portfolio({
25539
+ accountAddress,
25540
+ address: resolvedAddress,
25541
+ chainId
25542
+ }),
25543
+ queryFn: () => {
25544
+ const request = {
25545
+ accountAddress,
25546
+ address: resolvedAddress,
25547
+ chainId
25548
+ };
25549
+ return symmCoreClient.portfolio.getMetrics(request);
25550
+ },
25551
+ enabled: internalEnabled && (params.query?.enabled ?? true)
25552
+ });
25553
+ }
25554
+ function useResolvedNotificationsParams(params) {
25555
+ const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25556
+ const chainId = params.chainId ?? ctxChainId;
25557
+ return {
25558
+ symmCoreClient,
25559
+ chainId,
25560
+ userAddress: params.userAddress,
25561
+ query: params.query
25562
+ };
25563
+ }
25564
+ function useSymmNotificationsQuery(params) {
25565
+ const { symmCoreClient, chainId, userAddress, query } = useResolvedNotificationsParams(params);
25566
+ const internalEnabled = !!symmCoreClient && !!userAddress;
25567
+ return useQuery({
25568
+ ...query,
25569
+ queryKey: symmKeys.notifications(userAddress, chainId),
25570
+ queryFn: () => symmCoreClient.notifications.list({
25571
+ userAddress,
25572
+ chainId
25573
+ }),
25574
+ enabled: internalEnabled && (query?.enabled ?? true)
25575
+ });
25576
+ }
25577
+ function useSymmUnreadCountQuery(params) {
25578
+ const { symmCoreClient, chainId, userAddress, query } = useResolvedNotificationsParams(params);
25579
+ const internalEnabled = !!symmCoreClient && !!userAddress;
25580
+ return useQuery({
25581
+ ...query,
25582
+ queryKey: symmKeys.unreadCount(userAddress, chainId),
25583
+ queryFn: () => symmCoreClient.notifications.getUnreadCount({
25584
+ userAddress,
25585
+ chainId
25586
+ }),
25587
+ enabled: internalEnabled && (query?.enabled ?? true)
25588
+ });
25589
+ }
25590
+ function useSymmMarkReadNotificationMutation(params, options) {
25591
+ const { symmCoreClient, chainId, userAddress } = useResolvedNotificationsParams(params);
25592
+ const queryClient = useQueryClient();
25593
+ return useMutation({
25594
+ ...withSymmMutationConfig(options?.mutation, {
25595
+ onSuccess: () => {
25596
+ queryClient.invalidateQueries({
25597
+ queryKey: symmKeys.notifications(userAddress, chainId)
25598
+ });
25599
+ queryClient.invalidateQueries({
25600
+ queryKey: symmKeys.unreadCount(userAddress, chainId)
25601
+ });
25602
+ }
25603
+ }),
25604
+ mutationFn: async ({ id, timestamp }) => {
25605
+ if (!symmCoreClient || !userAddress) {
25606
+ throw new Error("symm-core client not available");
25699
25607
  }
25608
+ return symmCoreClient.notifications.markRead({
25609
+ id,
25610
+ timestamp,
25611
+ userAddress,
25612
+ chainId
25613
+ });
25700
25614
  }
25701
- }
25702
- ensureConnected() {
25703
- if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
25615
+ });
25616
+ }
25617
+ function useSymmPendingIds(params) {
25618
+ const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
25619
+ const { accountAddress } = params;
25620
+ const chainId = params.chainId ?? ctxChainId;
25621
+ const internalEnabled = !!symmCoreClient && !!accountAddress;
25622
+ return useQuery({
25623
+ ...params.query,
25624
+ queryKey: symmKeys.pendingIds(accountAddress, chainId),
25625
+ queryFn: () => symmCoreClient.positions.getPendingIds({
25626
+ address: accountAddress,
25627
+ chainId
25628
+ }),
25629
+ enabled: internalEnabled && (params.query?.enabled ?? true)
25630
+ });
25631
+ }
25632
+ function useSymmPendingInstantOpens(params) {
25633
+ const {
25634
+ symmCoreClient,
25635
+ chainId: ctxChainId
25636
+ } = useSymmContext();
25637
+ const { accountAddress, authToken: providedAuthToken } = params;
25638
+ const chainId = params.chainId ?? ctxChainId;
25639
+ const internalEnabled = !!symmCoreClient && !!accountAddress;
25640
+ return useQuery({
25641
+ ...params.query,
25642
+ queryKey: symmKeys.pendingInstantOpens(accountAddress, chainId),
25643
+ queryFn: async () => {
25644
+ const authToken = providedAuthToken ?? useSymmAuthStore.getState().getToken(accountAddress, chainId);
25645
+ if (!authToken) {
25646
+ throw new Error("failed to acquire auth token for pending instant opens");
25647
+ }
25648
+ return symmCoreClient.positions.getPendingInstantOpens({
25649
+ accountAddress,
25650
+ chainId,
25651
+ authToken
25652
+ });
25653
+ },
25654
+ enabled: internalEnabled && (params.query?.enabled ?? true)
25655
+ });
25656
+ }
25657
+ function useSymmTwapOrder(params) {
25658
+ const { symmCoreClient } = useSymmContext();
25659
+ const { orderId } = params;
25660
+ const internalEnabled = !!symmCoreClient && !!orderId;
25661
+ return useQuery({
25662
+ ...params.query,
25663
+ queryKey: symmKeys.twapOrder(orderId),
25664
+ queryFn: () => symmCoreClient.orders.getTwapOrder(orderId),
25665
+ enabled: internalEnabled && (params.query?.enabled ?? true)
25666
+ });
25667
+ }
25668
+ var useSymmWsStore = create((set) => ({
25669
+ isConnected: false,
25670
+ setConnected: (isConnected) => set({ isConnected })
25671
+ }));
25672
+
25673
+ // src/react/hooks/use-symm-ws.ts
25674
+ function asUnsubscribeFn(value) {
25675
+ return typeof value === "function" ? value : null;
25676
+ }
25677
+ function useSymmWs(params = {}) {
25678
+ const {
25679
+ symmCoreClient: ctxClient,
25680
+ address: ctxAddress,
25681
+ chainId: ctxChainId
25682
+ } = useSymmContext();
25683
+ const queryClient = useQueryClient();
25684
+ const isConnected = useSymmWsStore((state) => state.isConnected);
25685
+ const setConnected = useSymmWsStore((state) => state.setConnected);
25686
+ const symmCoreClient = params.symmCoreClient ?? ctxClient;
25687
+ const accountAddress = params.accountAddress ?? ctxAddress;
25688
+ const chainId = params.chainId ?? ctxChainId;
25689
+ useEffect(() => {
25690
+ if (!symmCoreClient || !accountAddress) {
25691
+ setConnected(false);
25704
25692
  return;
25705
25693
  }
25706
- this.connect();
25707
- }
25708
- connect() {
25709
- if (typeof WebSocket === "undefined") return;
25710
- this.intentionalClose = false;
25711
- this.ws = new WebSocket(BINANCE_WS_URL);
25712
- this.ws.onopen = () => {
25713
- this.reconnectAttempt = 0;
25714
- const activeStreams = Array.from(this.streams.keys());
25715
- if (activeStreams.length > 0) {
25716
- this.sendSubscribe(activeStreams);
25717
- }
25718
- if (this.pendingSubscribes.length > 0) {
25719
- this.sendSubscribe(this.pendingSubscribes);
25720
- this.pendingSubscribes = [];
25721
- }
25722
- };
25723
- this.ws.onmessage = (event) => {
25724
- try {
25725
- const data = JSON.parse(event.data);
25726
- this.handleMessage(data);
25727
- } catch {
25728
- }
25729
- };
25730
- this.ws.onclose = () => {
25731
- if (this.intentionalClose) return;
25732
- this.scheduleReconnect();
25694
+ const ws = symmCoreClient.ws;
25695
+ const addr = accountAddress;
25696
+ const unsubscribers = [];
25697
+ const removeOnConnect = ws.onConnect(() => setConnected(true));
25698
+ const removeOnDisconnect = ws.onDisconnect(() => setConnected(false));
25699
+ unsubscribers.push(removeOnConnect, removeOnDisconnect);
25700
+ const positionsUnsub = asUnsubscribeFn(
25701
+ ws.subscribeToPositions(addr, chainId, () => {
25702
+ queryClient.invalidateQueries({
25703
+ queryKey: ["symm", "positions"]
25704
+ });
25705
+ })
25706
+ );
25707
+ if (positionsUnsub) unsubscribers.push(positionsUnsub);
25708
+ const openOrdersUnsub = asUnsubscribeFn(
25709
+ ws.subscribeToOpenOrders(addr, chainId, () => {
25710
+ queryClient.invalidateQueries({
25711
+ queryKey: ["symm", "openOrders"]
25712
+ });
25713
+ })
25714
+ );
25715
+ if (openOrdersUnsub) unsubscribers.push(openOrdersUnsub);
25716
+ const tradesUnsub = asUnsubscribeFn(
25717
+ ws.subscribeToTrades(addr, chainId, () => {
25718
+ queryClient.invalidateQueries({
25719
+ queryKey: ["symm", "tradeHistory"]
25720
+ });
25721
+ })
25722
+ );
25723
+ if (tradesUnsub) unsubscribers.push(tradesUnsub);
25724
+ const accountSummaryUnsub = asUnsubscribeFn(
25725
+ ws.subscribeToAccountSummary(addr, chainId, () => {
25726
+ queryClient.invalidateQueries({
25727
+ queryKey: symmKeys.balances(accountAddress, chainId)
25728
+ });
25729
+ queryClient.invalidateQueries({
25730
+ queryKey: symmKeys.accountSummary(accountAddress, chainId)
25731
+ });
25732
+ })
25733
+ );
25734
+ if (accountSummaryUnsub) unsubscribers.push(accountSummaryUnsub);
25735
+ const notificationsUnsub = asUnsubscribeFn(
25736
+ ws.subscribeToNotifications(addr, chainId, () => {
25737
+ queryClient.invalidateQueries({
25738
+ queryKey: symmKeys.notifications(accountAddress, chainId)
25739
+ });
25740
+ queryClient.invalidateQueries({
25741
+ queryKey: symmKeys.unreadCount(accountAddress, chainId)
25742
+ });
25743
+ })
25744
+ );
25745
+ if (notificationsUnsub) unsubscribers.push(notificationsUnsub);
25746
+ const tpslUnsub = asUnsubscribeFn(
25747
+ ws.subscribeToTpsl(addr, chainId, () => {
25748
+ queryClient.invalidateQueries({ queryKey: ["symm", "tpslOrders"] });
25749
+ queryClient.invalidateQueries({ queryKey: ["symm", "openOrders"] });
25750
+ })
25751
+ );
25752
+ if (tpslUnsub) unsubscribers.push(tpslUnsub);
25753
+ const twapUnsub = asUnsubscribeFn(
25754
+ ws.subscribeToTwapOrders(addr, chainId, () => {
25755
+ queryClient.invalidateQueries({ queryKey: ["symm", "twapOrders"] });
25756
+ queryClient.invalidateQueries({ queryKey: ["symm", "openOrders"] });
25757
+ })
25758
+ );
25759
+ if (twapUnsub) unsubscribers.push(twapUnsub);
25760
+ const triggerOrdersUnsub = asUnsubscribeFn(
25761
+ ws.subscribeToTriggerOrders(addr, chainId, () => {
25762
+ queryClient.invalidateQueries({ queryKey: ["symm", "triggerOrders"] });
25763
+ queryClient.invalidateQueries({ queryKey: ["symm", "openOrders"] });
25764
+ })
25765
+ );
25766
+ if (triggerOrdersUnsub) unsubscribers.push(triggerOrdersUnsub);
25767
+ const executionsUnsub = asUnsubscribeFn(
25768
+ ws.subscribeToExecutions(addr, chainId, () => {
25769
+ queryClient.invalidateQueries({
25770
+ queryKey: ["symm", "positions"]
25771
+ });
25772
+ queryClient.invalidateQueries({
25773
+ queryKey: ["symm", "portfolio"]
25774
+ });
25775
+ })
25776
+ );
25777
+ if (executionsUnsub) unsubscribers.push(executionsUnsub);
25778
+ return () => {
25779
+ unsubscribers.forEach((unsubscribe) => unsubscribe());
25733
25780
  };
25734
- this.ws.onerror = () => {
25781
+ }, [symmCoreClient, accountAddress, chainId, queryClient, setConnected]);
25782
+ return { isConnected };
25783
+ }
25784
+ var STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USD", "USDT", "USDE", "USDH", "USDT0"]);
25785
+ function useSymmChartSelection(input) {
25786
+ const {
25787
+ longSymbol,
25788
+ shortSymbol,
25789
+ longWeight = 100,
25790
+ shortWeight = 100,
25791
+ longTokens: explicitLongTokens,
25792
+ shortTokens: explicitShortTokens
25793
+ } = input;
25794
+ return useMemo(() => {
25795
+ const longTokens = explicitLongTokens?.length ? explicitLongTokens : longSymbol ? [{ symbol: longSymbol, weight: longWeight }] : [];
25796
+ const shortTokens = explicitShortTokens?.length ? explicitShortTokens : shortSymbol ? [{ symbol: shortSymbol, weight: shortWeight }] : [];
25797
+ const selectedSymbols = [
25798
+ ...longTokens.map((t) => t.symbol),
25799
+ ...shortTokens.map((t) => t.symbol)
25800
+ ];
25801
+ const uniqueSelectedSymbols = Array.from(new Set(selectedSymbols));
25802
+ const longPrimarySymbol = longTokens[0]?.symbol ?? null;
25803
+ const shortPrimarySymbol = shortTokens[0]?.symbol ?? null;
25804
+ const longIsStable = longPrimarySymbol ? STABLE_SYMBOLS.has(longPrimarySymbol.toUpperCase()) : true;
25805
+ const shortIsStable = shortPrimarySymbol ? STABLE_SYMBOLS.has(shortPrimarySymbol.toUpperCase()) : true;
25806
+ let isSingleSided = false;
25807
+ let activeSide = null;
25808
+ let activeTokenSymbol;
25809
+ if (longTokens.length > 0 && !longIsStable && (shortTokens.length === 0 || shortIsStable)) {
25810
+ isSingleSided = true;
25811
+ activeSide = "long";
25812
+ activeTokenSymbol = longPrimarySymbol ?? void 0;
25813
+ } else if (shortTokens.length > 0 && !shortIsStable && (longTokens.length === 0 || longIsStable)) {
25814
+ isSingleSided = true;
25815
+ activeSide = "short";
25816
+ activeTokenSymbol = shortPrimarySymbol ?? void 0;
25817
+ }
25818
+ const longLabel = longTokens.map((token) => token.symbol).join("+") || "USDC";
25819
+ const shortLabel = shortTokens.map((token) => token.symbol).join("+") || "USDC";
25820
+ const pairLabel = `${longLabel}/${shortLabel}`;
25821
+ return {
25822
+ longTokens,
25823
+ shortTokens,
25824
+ isSingleSided,
25825
+ activeSide,
25826
+ activeTokenSymbol,
25827
+ pairLabel,
25828
+ selectedSymbols: uniqueSelectedSymbols
25735
25829
  };
25830
+ }, [
25831
+ explicitLongTokens,
25832
+ explicitShortTokens,
25833
+ longSymbol,
25834
+ shortSymbol,
25835
+ longWeight,
25836
+ shortWeight
25837
+ ]);
25838
+ }
25839
+
25840
+ // src/utils/chart-metrics.ts
25841
+ function getPositiveValue(metadata, field) {
25842
+ const value = metadata?.[field];
25843
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
25844
+ return null;
25736
25845
  }
25737
- handleMessage(data) {
25738
- if (data.e === "kline") {
25739
- const k = data.k;
25740
- const streamName = `${data.s.toLowerCase()}@kline_${k.i}`;
25741
- this.dispatchToStream(streamName, data);
25742
- } else if (data.e === "markPriceUpdate") {
25743
- const streamName = `${data.s.toLowerCase()}@markPrice@1s`;
25744
- this.dispatchToStream(streamName, data);
25846
+ return value;
25847
+ }
25848
+ function computeWeightedProduct(tokens, metadataMap, field, invert = false) {
25849
+ let product = 1;
25850
+ let hasToken = false;
25851
+ for (const token of tokens) {
25852
+ if (!(token.weight > 0)) {
25853
+ continue;
25745
25854
  }
25746
- }
25747
- dispatchToStream(streamName, data) {
25748
- const sub = this.streams.get(streamName);
25749
- if (!sub) return;
25750
- sub.callbacks.forEach((cb) => {
25751
- try {
25752
- cb(data);
25753
- } catch {
25754
- }
25755
- });
25756
- }
25757
- sendSubscribe(streams) {
25758
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
25759
- this.pendingSubscribes.push(...streams);
25760
- return;
25855
+ const price = getPositiveValue(metadataMap[token.symbol], field);
25856
+ if (price === null) {
25857
+ return null;
25761
25858
  }
25762
- this.ws.send(JSON.stringify({
25763
- method: "SUBSCRIBE",
25764
- params: streams,
25765
- id: Date.now()
25766
- }));
25859
+ hasToken = true;
25860
+ const exponent = invert ? -(token.weight / 100) : token.weight / 100;
25861
+ product *= Math.pow(price, exponent);
25767
25862
  }
25768
- sendUnsubscribe(streams) {
25769
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
25770
- this.pendingSubscribes = this.pendingSubscribes.filter(
25771
- (s) => !streams.includes(s)
25772
- );
25773
- return;
25774
- }
25775
- this.ws.send(JSON.stringify({
25776
- method: "UNSUBSCRIBE",
25777
- params: streams,
25778
- id: Date.now()
25779
- }));
25863
+ return hasToken ? product : 1;
25864
+ }
25865
+ function computePriceRatio({
25866
+ longTokens,
25867
+ shortTokens,
25868
+ longTokensMetadata,
25869
+ shortTokensMetadata
25870
+ }) {
25871
+ const firstLong = longTokens[0];
25872
+ const firstShort = shortTokens[0];
25873
+ const longPrice = firstLong ? getPositiveValue(longTokensMetadata[firstLong.symbol], "currentPrice") : null;
25874
+ const shortPrice = firstShort ? getPositiveValue(shortTokensMetadata[firstShort.symbol], "currentPrice") : null;
25875
+ if (longPrice !== null && shortPrice !== null) {
25876
+ return longPrice / shortPrice;
25780
25877
  }
25781
- scheduleReconnect() {
25782
- if (this.reconnectTimer) return;
25783
- if (this.streams.size === 0) return;
25784
- const delay = RECONNECT_DELAYS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS.length - 1)];
25785
- this.reconnectAttempt++;
25786
- this.reconnectTimer = setTimeout(() => {
25787
- this.reconnectTimer = null;
25788
- this.connect();
25789
- }, delay);
25878
+ if (longPrice !== null && shortTokens.length === 0) {
25879
+ return longPrice;
25790
25880
  }
25791
- };
25792
- var _instance = null;
25793
- function getBinanceWsManager() {
25794
- if (!_instance) {
25795
- _instance = new BinanceWsManager();
25881
+ if (shortPrice !== null && longTokens.length === 0) {
25882
+ return shortPrice;
25796
25883
  }
25797
- return _instance;
25884
+ return null;
25798
25885
  }
25799
-
25800
- // src/react/stores/use-binance-mark-price-store.ts
25801
- var refCounts = /* @__PURE__ */ new Map();
25802
- var streamUnsubs = /* @__PURE__ */ new Map();
25803
- var streamSymbols = /* @__PURE__ */ new Map();
25804
- function normalizeBinanceSymbol(symbol) {
25805
- return symbol.toUpperCase().trim();
25886
+ function computePriceRatio24h({
25887
+ longTokens,
25888
+ shortTokens,
25889
+ longTokensMetadata,
25890
+ shortTokensMetadata
25891
+ }) {
25892
+ const firstLong = longTokens[0];
25893
+ const firstShort = shortTokens[0];
25894
+ const longPrice = firstLong ? getPositiveValue(longTokensMetadata[firstLong.symbol], "prevDayPrice") : null;
25895
+ const shortPrice = firstShort ? getPositiveValue(shortTokensMetadata[firstShort.symbol], "prevDayPrice") : null;
25896
+ if (longPrice !== null && shortPrice !== null) {
25897
+ return longPrice / shortPrice;
25898
+ }
25899
+ if (longPrice !== null && shortTokens.length === 0) {
25900
+ return longPrice;
25901
+ }
25902
+ if (shortPrice !== null && longTokens.length === 0) {
25903
+ return shortPrice;
25904
+ }
25905
+ return null;
25806
25906
  }
25807
- function getNextRefCount(binanceSymbol) {
25808
- return (refCounts.get(binanceSymbol) ?? 0) + 1;
25907
+ function computeWeightedRatio({
25908
+ longTokens,
25909
+ shortTokens,
25910
+ longTokensMetadata,
25911
+ shortTokensMetadata
25912
+ }) {
25913
+ const longProduct = computeWeightedProduct(
25914
+ longTokens,
25915
+ longTokensMetadata,
25916
+ "currentPrice"
25917
+ );
25918
+ const shortProduct = computeWeightedProduct(
25919
+ shortTokens,
25920
+ shortTokensMetadata,
25921
+ "currentPrice",
25922
+ true
25923
+ );
25924
+ if (longProduct === null || shortProduct === null) {
25925
+ return null;
25926
+ }
25927
+ return longProduct * shortProduct;
25809
25928
  }
25810
- function getPrevRefCount(binanceSymbol) {
25811
- return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);
25929
+ function computeWeightedRatio24h({
25930
+ longTokens,
25931
+ shortTokens,
25932
+ longTokensMetadata,
25933
+ shortTokensMetadata
25934
+ }) {
25935
+ const longProduct = computeWeightedProduct(
25936
+ longTokens,
25937
+ longTokensMetadata,
25938
+ "prevDayPrice"
25939
+ );
25940
+ const shortProduct = computeWeightedProduct(
25941
+ shortTokens,
25942
+ shortTokensMetadata,
25943
+ "prevDayPrice",
25944
+ true
25945
+ );
25946
+ if (longProduct === null || shortProduct === null) {
25947
+ return null;
25948
+ }
25949
+ return longProduct * shortProduct;
25812
25950
  }
25813
- var useBinanceMarkPriceStore = create((set) => ({
25814
- markPrices: {},
25815
- subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
25816
- const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
25817
- const nextRefCount = getNextRefCount(binanceSymbol);
25818
- refCounts.set(binanceSymbol, nextRefCount);
25819
- const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
25820
- symbols.add(symmSymbol);
25821
- streamSymbols.set(binanceSymbol, symbols);
25822
- if (nextRefCount === 1) {
25823
- const wsManager = getBinanceWsManager();
25824
- const unsubscribe = wsManager.subscribeMarkPrice(binanceSymbol, (data) => {
25825
- const canonicalSymbol = normalizeBinanceSymbol(data.symbol);
25826
- const mappedSymbols = streamSymbols.get(canonicalSymbol);
25827
- if (!mappedSymbols || mappedSymbols.size === 0) return;
25828
- set((state) => {
25829
- const nextMarkPrices = { ...state.markPrices };
25830
- mappedSymbols.forEach((mappedSymbol) => {
25831
- nextMarkPrices[mappedSymbol] = data.markPrice;
25832
- });
25833
- return { markPrices: nextMarkPrices };
25834
- });
25835
- });
25836
- streamUnsubs.set(binanceSymbol, unsubscribe);
25837
- }
25838
- },
25839
- unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
25840
- const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
25841
- const symbols = streamSymbols.get(binanceSymbol);
25842
- if (symbols) {
25843
- symbols.delete(symmSymbol);
25844
- if (symbols.size === 0) {
25845
- streamSymbols.delete(binanceSymbol);
25846
- } else {
25847
- streamSymbols.set(binanceSymbol, symbols);
25848
- }
25951
+ function computeNetFundingSum({
25952
+ longTokens,
25953
+ shortTokens,
25954
+ longTokensMetadata,
25955
+ shortTokensMetadata
25956
+ }) {
25957
+ let funding = 0;
25958
+ for (const token of longTokens) {
25959
+ const value = longTokensMetadata[token.symbol]?.netFunding;
25960
+ if (typeof value === "number" && Number.isFinite(value)) {
25961
+ funding += value;
25849
25962
  }
25850
- const nextRefCount = getPrevRefCount(binanceSymbol);
25851
- if (nextRefCount === 0) {
25852
- const unsubscribe = streamUnsubs.get(binanceSymbol);
25853
- if (unsubscribe) unsubscribe();
25854
- streamUnsubs.delete(binanceSymbol);
25855
- refCounts.delete(binanceSymbol);
25856
- } else {
25857
- refCounts.set(binanceSymbol, nextRefCount);
25963
+ }
25964
+ for (const token of shortTokens) {
25965
+ const value = shortTokensMetadata[token.symbol]?.netFunding;
25966
+ if (typeof value === "number" && Number.isFinite(value)) {
25967
+ funding += value;
25858
25968
  }
25859
- set((state) => {
25860
- if (state.markPrices[symmSymbol] == null) return state;
25861
- const nextMarkPrices = { ...state.markPrices };
25862
- delete nextMarkPrices[symmSymbol];
25863
- return { markPrices: nextMarkPrices };
25864
- });
25865
25969
  }
25866
- }));
25970
+ return funding;
25971
+ }
25867
25972
 
25868
25973
  // src/react/hooks/use-symm-token-selection-metadata.ts
25869
- async function fetchTickerSnapshot(symbol) {
25974
+ async function fetchTickerSnapshot2(symbol) {
25870
25975
  const resolution = resolveBinanceSymbol(symbol);
25871
25976
  if (!resolution.binanceSymbol) return null;
25872
25977
  const ticker = await fetch24hrTicker(resolution.binanceSymbol);
@@ -25922,7 +26027,7 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
25922
26027
  const results = await Promise.all(
25923
26028
  allSymbols.map(async ({ symbol }) => ({
25924
26029
  symbol,
25925
- ticker: await fetchTickerSnapshot(symbol)
26030
+ ticker: await fetchTickerSnapshot2(symbol)
25926
26031
  }))
25927
26032
  );
25928
26033
  const tickerSnapshots = {};
@@ -26395,6 +26500,6 @@ function getSymmErrorMessage(error) {
26395
26500
  return "An unexpected error occurred.";
26396
26501
  }
26397
26502
 
26398
- export { SymmProvider, getSymmErrorMessage, symmKeys, useBinanceMarkPriceStore, useSymmAccountData, useSymmAccountSummary, useSymmAccountsApi, useSymmAccountsLength, useSymmAccountsQuery, useSymmAccountsWithPositions, useSymmAllocateCollateralMutation, useSymmApprovalQuery, useSymmApproveMutation, useSymmAuth, useSymmAuthStore, useSymmAvailableMargin, useSymmBalances, useSymmCancelClose, useSymmCancelOpenMutation, useSymmCancelTpslMutation, useSymmCancelTwapOrderMutation, useSymmChartCandles, useSymmChartSelection, useSymmClearTriggerConfigMutation, useSymmCloseAllPositionsMutation, useSymmCloseOrder, useSymmClosePositionMutation, useSymmContext, useSymmCoreClient, useSymmCreateAccountMutation, useSymmDeallocateCollateralMutation, useSymmDelegation, useSymmDepositAndAllocateMutation, useSymmDepositMutation, useSymmEditAccountNameMutation, useSymmFunding, useSymmFundingHistory, useSymmFundingPayments, useSymmHedgerMarketById, useSymmHedgerMarketBySymbol, useSymmHedgerMarkets, useSymmInstantTradeEnsureReadyMutation, useSymmInstantTradeExecuteMutation, useSymmInternalTransferCollateralMutation, useSymmLockedParams, useSymmMarkReadNotificationMutation, useSymmMarkets, useSymmNotificationsQuery, useSymmOpenBasketMutation, useSymmOpenOrders, useSymmPendingIds, useSymmPendingInstantOpens, useSymmPerformanceOverlays, useSymmPortfolio, useSymmPositions, useSymmSetTpslMutation, useSymmSetTriggerConfigMutation, useSymmSignTermsMutation, useSymmSignatureQuery, useSymmTokenSelectionMetadata, useSymmTpslOrders, useSymmTradeHistory, useSymmTriggerConfigQuery, useSymmTriggerOrders, useSymmTwapOrder, useSymmTwapOrdersQuery, useSymmUnreadCountQuery, useSymmUpdatePositionMutation, useSymmWithdraw, useSymmWs, useSymmWsStore };
26503
+ export { SymmProvider, getSymmErrorMessage, symmKeys, useBinanceMarkPriceStore, useSymmAccountData, useSymmAccountSummary, useSymmAccountsApi, useSymmAccountsLength, useSymmAccountsQuery, useSymmAccountsWithPositions, useSymmAllocateCollateralMutation, useSymmApprovalQuery, useSymmApproveMutation, useSymmAuth, useSymmAuthStore, useSymmAvailableMargin, useSymmBalances, useSymmCancelClose, useSymmCancelOpenMutation, useSymmCancelTpslMutation, useSymmCancelTwapOrderMutation, useSymmChartCandles, useSymmChartSelection, useSymmClearTriggerConfigMutation, useSymmCloseAllPositionsMutation, useSymmCloseOrder, useSymmClosePositionMutation, useSymmContext, useSymmCoreClient, useSymmCreateAccountMutation, useSymmDeallocateCollateralMutation, useSymmDelegation, useSymmDepositAndAllocateMutation, useSymmDepositMutation, useSymmEditAccountNameMutation, useSymmFunding, useSymmFundingHistory, useSymmFundingPayments, useSymmHedgerMarketById, useSymmHedgerMarketBySymbol, useSymmHedgerMarkets, useSymmInstantTradeEnsureReadyMutation, useSymmInstantTradeExecuteMutation, useSymmInternalTransferCollateralMutation, useSymmLockedParams, useSymmMarkReadNotificationMutation, useSymmMarkets, useSymmNotificationsQuery, useSymmOpenBasketMutation, useSymmOpenOrders, useSymmPendingIds, useSymmPendingInstantOpens, useSymmPerformanceOverlays, useSymmPortfolio, useSymmPositions, useSymmSetTpslMutation, useSymmSetTriggerConfigMutation, useSymmSignTermsMutation, useSymmSignatureQuery, useSymmTokenSelectionMarkets, useSymmTokenSelectionMetadata, useSymmTpslOrders, useSymmTradeHistory, useSymmTriggerConfigQuery, useSymmTriggerOrders, useSymmTwapOrder, useSymmTwapOrdersQuery, useSymmUnreadCountQuery, useSymmUpdatePositionMutation, useSymmWithdraw, useSymmWs, useSymmWsStore };
26399
26504
  //# sourceMappingURL=index.mjs.map
26400
26505
  //# sourceMappingURL=index.mjs.map