@pear-protocol/symmio-client 0.1.8 → 0.1.9

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.
@@ -833,14 +833,14 @@ interface UseSymmTokenSelectionMetadataReturn {
833
833
  priceRatio24h: number;
834
834
  sumNetFunding: number;
835
835
  }
836
+ interface UseSymmTokenSelectionMetadataOptions {
837
+ enabled?: boolean;
838
+ }
836
839
  /**
837
840
  * Fetches Binance market data for selected SYMM chart tokens and derives
838
841
  * chart header metrics (price ratio, weighted ratio, etc.).
839
842
  */
840
- declare function useSymmTokenSelectionMetadata(selection: SymmChartSelection, options?: {
841
- enabled?: boolean;
842
- query?: SymmQueryConfig;
843
- }): UseSymmTokenSelectionMetadataReturn;
843
+ declare function useSymmTokenSelectionMetadata(selection: SymmChartSelection, options?: UseSymmTokenSelectionMetadataOptions): UseSymmTokenSelectionMetadataReturn;
844
844
 
845
845
  /**
846
846
  * Candle interval type matching the chart system.
@@ -833,14 +833,14 @@ interface UseSymmTokenSelectionMetadataReturn {
833
833
  priceRatio24h: number;
834
834
  sumNetFunding: number;
835
835
  }
836
+ interface UseSymmTokenSelectionMetadataOptions {
837
+ enabled?: boolean;
838
+ }
836
839
  /**
837
840
  * Fetches Binance market data for selected SYMM chart tokens and derives
838
841
  * chart header metrics (price ratio, weighted ratio, etc.).
839
842
  */
840
- declare function useSymmTokenSelectionMetadata(selection: SymmChartSelection, options?: {
841
- enabled?: boolean;
842
- query?: SymmQueryConfig;
843
- }): UseSymmTokenSelectionMetadataReturn;
843
+ declare function useSymmTokenSelectionMetadata(selection: SymmChartSelection, options?: UseSymmTokenSelectionMetadataOptions): UseSymmTokenSelectionMetadataReturn;
844
844
 
845
845
  /**
846
846
  * Candle interval type matching the chart system.
@@ -25795,137 +25795,128 @@ function getBinanceWsManager() {
25795
25795
  }
25796
25796
 
25797
25797
  // src/react/hooks/use-symm-token-selection-metadata.ts
25798
- var EMPTY_TOKEN_METADATA = {};
25799
- var EMPTY_MARK_PRICES = {};
25800
- async function fetchTokenMetadata(symbol) {
25798
+ async function fetchTickerSnapshot(symbol) {
25801
25799
  const resolution = resolveBinanceSymbol(symbol);
25802
25800
  if (!resolution.binanceSymbol) return null;
25803
25801
  const ticker = await fetch24hrTicker(resolution.binanceSymbol);
25804
25802
  if (!ticker) return null;
25805
25803
  return {
25806
- lastPrice: ticker.lastPrice,
25807
- prevDayPrice: ticker.prevClosePrice
25804
+ prevClosePrice: ticker.prevClosePrice
25808
25805
  };
25809
25806
  }
25810
- function createLiveMetadata(symbol, snapshot, liveMarkPrices) {
25811
- if (!snapshot) return null;
25812
- const liveMarkPrice = liveMarkPrices[symbol]?.markPrice ?? snapshot.lastPrice;
25813
- const priceChange24h = liveMarkPrice - snapshot.prevDayPrice;
25814
- const priceChange24hPercent = snapshot.prevDayPrice !== 0 ? (liveMarkPrice - snapshot.prevDayPrice) / snapshot.prevDayPrice * 100 : 0;
25807
+ function toSymmTokenMetadata(currentPrice, prevDayPrice) {
25808
+ const priceChange24h = currentPrice - prevDayPrice;
25809
+ const priceChange24hPercent = prevDayPrice !== 0 ? (currentPrice - prevDayPrice) / prevDayPrice * 100 : 0;
25815
25810
  return {
25816
- currentPrice: liveMarkPrice,
25817
- prevDayPrice: snapshot.prevDayPrice,
25811
+ currentPrice,
25812
+ prevDayPrice,
25818
25813
  priceChange24h,
25819
25814
  priceChange24hPercent,
25820
25815
  netFunding: 0,
25821
- markPrice: liveMarkPrice
25816
+ // SYMM funding integration deferred
25817
+ markPrice: currentPrice
25822
25818
  };
25823
25819
  }
25824
- function useSymmTokenSelectionMetadata(selection, options) {
25820
+ function useSymmTokenSelectionMetadata(selection, options = {}) {
25825
25821
  const { longTokens, shortTokens, selectedSymbols } = selection;
25822
+ const { enabled = true } = options;
25826
25823
  const unsupportedSymbols = react.useMemo(
25827
25824
  () => getUnsupportedBinanceSymbols(selectedSymbols),
25828
25825
  [selectedSymbols]
25829
25826
  );
25830
25827
  const isUnsupported = unsupportedSymbols.length > 0;
25831
25828
  const unavailableReason = isUnsupported ? `Binance market data is unavailable for ${unsupportedSymbols.join(", ")}.` : null;
25832
- const externalEnabled = options?.enabled ?? true;
25833
- const internalEnabled = externalEnabled && selectedSymbols.length > 0 && !isUnsupported;
25834
- const [liveMarkPrices, setLiveMarkPrices] = react.useState(EMPTY_MARK_PRICES);
25835
25829
  const symbolsKey = [...selectedSymbols].sort().join(",");
25836
- const positionKey = [
25837
- longTokens.map((token) => `${token.symbol}:${token.weight}`).join("|"),
25838
- shortTokens.map((token) => `${token.symbol}:${token.weight}`).join("|")
25839
- ].join("::");
25830
+ const symbolToBinanceMap = react.useMemo(
25831
+ () => new Map(
25832
+ selectedSymbols.map(
25833
+ (symbol) => [symbol, resolveBinanceSymbol(symbol).binanceSymbol]
25834
+ ).filter((entry) => !!entry[1])
25835
+ ),
25836
+ [selectedSymbols]
25837
+ );
25838
+ const [liveMarkPrices, setLiveMarkPrices] = react.useState(
25839
+ {}
25840
+ );
25840
25841
  const query = reactQuery.useQuery({
25841
- ...options?.query,
25842
- queryKey: symmKeys.chartMetadata(symbolsKey, positionKey),
25842
+ queryKey: ["symm", "chart-metadata", symbolsKey],
25843
25843
  queryFn: async () => {
25844
25844
  const allSymbols = [
25845
25845
  ...longTokens.map((t) => ({ symbol: t.symbol, side: "long" })),
25846
- ...shortTokens.map((t) => ({ symbol: t.symbol, side: "short" }))
25846
+ ...shortTokens.map((t) => ({
25847
+ symbol: t.symbol,
25848
+ side: "short"
25849
+ }))
25847
25850
  ];
25848
25851
  const results = await Promise.all(
25849
- allSymbols.map(async ({ symbol, side }) => ({
25852
+ allSymbols.map(async ({ symbol }) => ({
25850
25853
  symbol,
25851
- side,
25852
- metadata: await fetchTokenMetadata(symbol)
25854
+ ticker: await fetchTickerSnapshot(symbol)
25853
25855
  }))
25854
25856
  );
25855
- const longMeta = {};
25856
- const shortMeta = {};
25857
- for (const { symbol, side, metadata } of results) {
25858
- if (side === "long") longMeta[symbol] = metadata;
25859
- else shortMeta[symbol] = metadata;
25857
+ const tickerSnapshots = {};
25858
+ for (const { symbol, ticker } of results) {
25859
+ tickerSnapshots[symbol] = ticker;
25860
25860
  }
25861
- return { longMeta, shortMeta };
25861
+ return { tickerSnapshots };
25862
25862
  },
25863
- enabled: internalEnabled && (options?.query?.enabled ?? true),
25864
- staleTime: options?.query?.staleTime ?? 3e5,
25865
- refetchInterval: options?.query?.refetchInterval ?? false
25863
+ enabled: enabled && selectedSymbols.length > 0 && !isUnsupported,
25864
+ staleTime: Infinity,
25865
+ gcTime: 5 * 6e4
25866
25866
  });
25867
25867
  react.useEffect(() => {
25868
- if (!internalEnabled) {
25869
- setLiveMarkPrices(EMPTY_MARK_PRICES);
25868
+ if (!enabled || isUnsupported || selectedSymbols.length === 0) {
25869
+ setLiveMarkPrices({});
25870
25870
  return;
25871
25871
  }
25872
- const wsManager = getBinanceWsManager();
25873
- const activeSymbols = selectedSymbols.map((symbol) => ({
25874
- symbol,
25875
- binanceSymbol: resolveBinanceSymbol(symbol).binanceSymbol
25876
- })).filter((entry) => !!entry.binanceSymbol);
25877
- setLiveMarkPrices((previous) => {
25878
- const nextEntries = activeSymbols.map(({ symbol }) => [symbol, previous[symbol]]).filter(([, value]) => value != null);
25879
- return Object.fromEntries(nextEntries);
25880
- });
25881
- const unsubscribers = activeSymbols.map(({ symbol, binanceSymbol }) => wsManager.subscribeMarkPrice(binanceSymbol, (data) => {
25882
- setLiveMarkPrices((previous) => {
25883
- const current = previous[symbol];
25884
- if (current?.markPrice === data.markPrice && current?.indexPrice === data.indexPrice && current?.time === data.time) {
25885
- return previous;
25872
+ setLiveMarkPrices((current) => {
25873
+ const next = {};
25874
+ for (const symbol of selectedSymbols) {
25875
+ if (current[symbol] != null) {
25876
+ next[symbol] = current[symbol];
25886
25877
  }
25887
- return {
25888
- ...previous,
25889
- [symbol]: data
25890
- };
25891
- });
25892
- }));
25878
+ }
25879
+ return next;
25880
+ });
25881
+ const wsManager = getBinanceWsManager();
25882
+ const unsubscribes = Array.from(symbolToBinanceMap.entries()).map(
25883
+ ([symbol, binanceSymbol]) => wsManager.subscribeMarkPrice(binanceSymbol, (data) => {
25884
+ setLiveMarkPrices((current) => {
25885
+ if (current[symbol] === data.markPrice) return current;
25886
+ return {
25887
+ ...current,
25888
+ [symbol]: data.markPrice
25889
+ };
25890
+ });
25891
+ })
25892
+ );
25893
25893
  return () => {
25894
- unsubscribers.forEach((unsubscribe) => unsubscribe());
25894
+ for (const unsubscribe of unsubscribes) {
25895
+ unsubscribe();
25896
+ }
25895
25897
  };
25896
- }, [internalEnabled, selectedSymbols]);
25898
+ }, [enabled, isUnsupported, selectedSymbols, symbolToBinanceMap]);
25897
25899
  return react.useMemo(() => {
25898
- if (!externalEnabled) {
25899
- return {
25900
- isLoading: false,
25901
- isPriceDataReady: false,
25902
- isUnsupported,
25903
- unsupportedSymbols,
25904
- unavailableReason,
25905
- longTokensMetadata: EMPTY_TOKEN_METADATA,
25906
- shortTokensMetadata: EMPTY_TOKEN_METADATA,
25907
- weightedRatio: 0,
25908
- weightedRatio24h: 0,
25909
- priceRatio: 0,
25910
- priceRatio24h: 0,
25911
- sumNetFunding: 0
25912
- };
25900
+ const tickerSnapshots = query.data?.tickerSnapshots ?? {};
25901
+ const longMeta = {};
25902
+ const shortMeta = {};
25903
+ const isLoading = query.isLoading;
25904
+ for (const token of longTokens) {
25905
+ const currentPrice = liveMarkPrices[token.symbol];
25906
+ const ticker = tickerSnapshots[token.symbol];
25907
+ longMeta[token.symbol] = currentPrice != null && ticker ? toSymmTokenMetadata(currentPrice, ticker.prevClosePrice) : null;
25913
25908
  }
25914
- const longMeta = Object.fromEntries(
25915
- longTokens.map((token) => [
25916
- token.symbol,
25917
- createLiveMetadata(token.symbol, query.data?.longMeta?.[token.symbol], liveMarkPrices)
25918
- ])
25909
+ for (const token of shortTokens) {
25910
+ const currentPrice = liveMarkPrices[token.symbol];
25911
+ const ticker = tickerSnapshots[token.symbol];
25912
+ shortMeta[token.symbol] = currentPrice != null && ticker ? toSymmTokenMetadata(currentPrice, ticker.prevClosePrice) : null;
25913
+ }
25914
+ const allLongReady = longTokens.every(
25915
+ (t) => longMeta[t.symbol]?.currentPrice != null
25919
25916
  );
25920
- const shortMeta = Object.fromEntries(
25921
- shortTokens.map((token) => [
25922
- token.symbol,
25923
- createLiveMetadata(token.symbol, query.data?.shortMeta?.[token.symbol], liveMarkPrices)
25924
- ])
25917
+ const allShortReady = shortTokens.every(
25918
+ (t) => shortMeta[t.symbol]?.currentPrice != null
25925
25919
  );
25926
- const isLoading = query.isLoading;
25927
- const allLongReady = longTokens.every((t) => longMeta[t.symbol]?.currentPrice != null);
25928
- const allShortReady = shortTokens.every((t) => shortMeta[t.symbol]?.currentPrice != null);
25929
25920
  const isPriceDataReady = !isLoading && !isUnsupported && (selectedSymbols.length === 0 || allLongReady && allShortReady);
25930
25921
  const metricInput = {
25931
25922
  longTokens,
@@ -25953,16 +25944,15 @@ function useSymmTokenSelectionMetadata(selection, options) {
25953
25944
  sumNetFunding
25954
25945
  };
25955
25946
  }, [
25956
- externalEnabled,
25957
25947
  isUnsupported,
25958
25948
  longTokens,
25959
- liveMarkPrices,
25960
25949
  query.data,
25961
25950
  query.isLoading,
25962
25951
  shortTokens,
25963
25952
  unavailableReason,
25964
25953
  unsupportedSymbols,
25965
- selectedSymbols.length
25954
+ selectedSymbols.length,
25955
+ liveMarkPrices
25966
25956
  ]);
25967
25957
  }
25968
25958