@pear-protocol/hyperliquid-sdk 0.1.29 → 0.1.31

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.js CHANGED
@@ -972,6 +972,23 @@ const useUserSelection$1 = create((set, get) => ({
972
972
  resetToDefaults: () => set((prev) => ({ ...prev, ...DEFAULT_STATE })),
973
973
  }));
974
974
 
975
+ const STABLE_QUOTE_SYMBOLS = new Set([
976
+ 'USDC',
977
+ 'USDH',
978
+ 'USDE',
979
+ 'USDT',
980
+ 'USDT0',
981
+ ]);
982
+ const isStableQuoteSymbol = (symbol) => STABLE_QUOTE_SYMBOLS.has(symbol.toUpperCase());
983
+ const hasNonStableTokens = (tokens) => tokens.some((token) => !isStableQuoteSymbol(token.symbol));
984
+ const isStableOnlySide = (tokens) => tokens.length === 0 || tokens.every((token) => isStableQuoteSymbol(token.symbol));
985
+ const isSingleAssetSideAgainstStableQuote = (longTokens, shortTokens) => {
986
+ const longHasNonStable = hasNonStableTokens(longTokens);
987
+ const shortHasNonStable = hasNonStableTokens(shortTokens);
988
+ return ((longHasNonStable && isStableOnlySide(shortTokens)) ||
989
+ (shortHasNonStable && isStableOnlySide(longTokens)));
990
+ };
991
+
975
992
  const CANDLE_STALE_WARNING_MS = 90000;
976
993
  const getCandleSubscriptionKey = (coin, interval) => `${coin}:${interval}`;
977
994
  const debugHyperliquidWs = (message, payload) => {
@@ -993,7 +1010,9 @@ const useHyperliquidNativeWebSocket = ({ address, enabled = true, onUserFills, }
993
1010
  const userSummary = useUserData((state) => state.accountSummary);
994
1011
  const longTokens = useUserSelection$1((s) => s.longTokens);
995
1012
  const shortTokens = useUserSelection$1((s) => s.shortTokens);
996
- const selectedTokenSymbols = useMemo(() => [...longTokens, ...shortTokens].map((t) => t.symbol), [longTokens, shortTokens]);
1013
+ const selectedTokenSymbols = useMemo(() => [...longTokens, ...shortTokens]
1014
+ .map((t) => t.symbol)
1015
+ .filter((symbol) => !isStableQuoteSymbol(symbol)), [longTokens, shortTokens]);
997
1016
  const [lastError, setLastError] = useState(null);
998
1017
  const [subscribedAddress, setSubscribedAddress] = useState(null);
999
1018
  const [subscribedTokens, setSubscribedTokens] = useState([]);
@@ -1904,7 +1923,7 @@ const useTokenSelectionMetadataStore = create((set) => ({
1904
1923
  ...longTokensMetadata,
1905
1924
  ...shortTokensMetadata,
1906
1925
  };
1907
- return allTokens.some((token) => !allMetadata[token.symbol]);
1926
+ return allTokens.some((token) => !isStableQuoteSymbol(token.symbol) && !allMetadata[token.symbol]);
1908
1927
  })();
1909
1928
  // Open interest and volume (from market data for matching asset basket)
1910
1929
  const { openInterest, volume } = (() => {
@@ -1927,9 +1946,15 @@ const useTokenSelectionMetadataStore = create((set) => ({
1927
1946
  ? { openInterest: match.openInterest, volume: match.volume }
1928
1947
  : empty;
1929
1948
  })();
1949
+ const getPrice = (metadata, symbol, field) => {
1950
+ var _a;
1951
+ if (isStableQuoteSymbol(symbol))
1952
+ return 1;
1953
+ return (_a = metadata === null || metadata === void 0 ? void 0 : metadata[field]) !== null && _a !== void 0 ? _a : 0;
1954
+ };
1955
+ const shouldUseAbsoluteQuoteRatio = isSingleAssetSideAgainstStableQuote(longTokens, shortTokens);
1930
1956
  // Price ratio (only when exactly one long and one short)
1931
1957
  const { priceRatio, priceRatio24h } = (() => {
1932
- var _a, _b, _c, _d;
1933
1958
  if (longTokens.length !== 1 || shortTokens.length !== 1) {
1934
1959
  return { priceRatio: 1, priceRatio24h: 1 };
1935
1960
  }
@@ -1937,10 +1962,22 @@ const useTokenSelectionMetadataStore = create((set) => ({
1937
1962
  const shortSymbol = shortTokens[0].symbol;
1938
1963
  const longMeta = longTokensMetadata[longSymbol];
1939
1964
  const shortMeta = shortTokensMetadata[shortSymbol];
1940
- const currentLong = (_a = longMeta === null || longMeta === void 0 ? void 0 : longMeta.currentPrice) !== null && _a !== void 0 ? _a : 0;
1941
- const currentShort = (_b = shortMeta === null || shortMeta === void 0 ? void 0 : shortMeta.currentPrice) !== null && _b !== void 0 ? _b : 0;
1942
- const prevLong = (_c = longMeta === null || longMeta === void 0 ? void 0 : longMeta.prevDayPrice) !== null && _c !== void 0 ? _c : 0;
1943
- const prevShort = (_d = shortMeta === null || shortMeta === void 0 ? void 0 : shortMeta.prevDayPrice) !== null && _d !== void 0 ? _d : 0;
1965
+ const currentLong = getPrice(longMeta, longSymbol, "currentPrice");
1966
+ const currentShort = getPrice(shortMeta, shortSymbol, "currentPrice");
1967
+ const prevLong = getPrice(longMeta, longSymbol, "prevDayPrice");
1968
+ const prevShort = getPrice(shortMeta, shortSymbol, "prevDayPrice");
1969
+ if (isStableQuoteSymbol(longSymbol) && !isStableQuoteSymbol(shortSymbol)) {
1970
+ return {
1971
+ priceRatio: currentShort || 1,
1972
+ priceRatio24h: prevShort || 1,
1973
+ };
1974
+ }
1975
+ if (isStableQuoteSymbol(shortSymbol) && !isStableQuoteSymbol(longSymbol)) {
1976
+ return {
1977
+ priceRatio: currentLong || 1,
1978
+ priceRatio24h: prevLong || 1,
1979
+ };
1980
+ }
1944
1981
  const ratio = currentShort !== 0 ? currentLong / currentShort : 1;
1945
1982
  const ratio24h = prevShort !== 0 ? prevLong / prevShort : 1;
1946
1983
  return { priceRatio: ratio, priceRatio24h: ratio24h };
@@ -1951,16 +1988,20 @@ const useTokenSelectionMetadataStore = create((set) => ({
1951
1988
  let shortProduct = 1;
1952
1989
  longTokens.forEach((token) => {
1953
1990
  const metadata = longTokensMetadata[token.symbol];
1954
- if ((metadata === null || metadata === void 0 ? void 0 : metadata.currentPrice) && token.weight > 0) {
1991
+ const price = getPrice(metadata, token.symbol, "currentPrice");
1992
+ if (price && token.weight > 0) {
1955
1993
  const weightFactor = token.weight / 100;
1956
- longProduct *= Math.pow(metadata.currentPrice, weightFactor);
1994
+ longProduct *= Math.pow(price, weightFactor);
1957
1995
  }
1958
1996
  });
1959
1997
  shortTokens.forEach((token) => {
1960
1998
  const metadata = shortTokensMetadata[token.symbol];
1961
- if ((metadata === null || metadata === void 0 ? void 0 : metadata.currentPrice) && token.weight > 0) {
1962
- const weightFactor = token.weight / 100;
1963
- shortProduct *= Math.pow(metadata.currentPrice, -weightFactor);
1999
+ const price = getPrice(metadata, token.symbol, "currentPrice");
2000
+ if (price && token.weight > 0) {
2001
+ const weightFactor = shouldUseAbsoluteQuoteRatio
2002
+ ? token.weight / 100
2003
+ : -(token.weight / 100);
2004
+ shortProduct *= Math.pow(price, weightFactor);
1964
2005
  }
1965
2006
  });
1966
2007
  return longProduct * shortProduct;
@@ -1971,16 +2012,20 @@ const useTokenSelectionMetadataStore = create((set) => ({
1971
2012
  let shortProduct = 1;
1972
2013
  longTokens.forEach((token) => {
1973
2014
  const metadata = longTokensMetadata[token.symbol];
1974
- if ((metadata === null || metadata === void 0 ? void 0 : metadata.prevDayPrice) && token.weight > 0) {
2015
+ const price = getPrice(metadata, token.symbol, "prevDayPrice");
2016
+ if (price && token.weight > 0) {
1975
2017
  const weightFactor = token.weight / 100;
1976
- longProduct *= Math.pow(metadata.prevDayPrice, weightFactor);
2018
+ longProduct *= Math.pow(price, weightFactor);
1977
2019
  }
1978
2020
  });
1979
2021
  shortTokens.forEach((token) => {
1980
2022
  const metadata = shortTokensMetadata[token.symbol];
1981
- if ((metadata === null || metadata === void 0 ? void 0 : metadata.prevDayPrice) && token.weight > 0) {
1982
- const weightFactor = token.weight / 100;
1983
- shortProduct *= Math.pow(metadata.prevDayPrice, -weightFactor);
2023
+ const price = getPrice(metadata, token.symbol, "prevDayPrice");
2024
+ if (price && token.weight > 0) {
2025
+ const weightFactor = shouldUseAbsoluteQuoteRatio
2026
+ ? token.weight / 100
2027
+ : -(token.weight / 100);
2028
+ shortProduct *= Math.pow(price, weightFactor);
1984
2029
  }
1985
2030
  });
1986
2031
  return longProduct * shortProduct;
@@ -6440,7 +6485,7 @@ const useHistoricalPriceData = () => {
6440
6485
  const prevIntervalRef = useRef(null);
6441
6486
  // Get all tokens from long and short selections
6442
6487
  const getAllTokens = useCallback(() => {
6443
- return [...longTokens, ...shortTokens];
6488
+ return [...longTokens, ...shortTokens].filter((token) => !isStableQuoteSymbol(token.symbol));
6444
6489
  }, [longTokens, shortTokens]);
6445
6490
  // Track token and interval changes and manage cache accordingly
6446
6491
  useEffect(() => {
@@ -6578,10 +6623,10 @@ const createCandleLookups = (tokenCandles) => {
6578
6623
  const calculateWeightedRatio = (longTokens, shortTokens, candleLookups, timestamp, priceType) => {
6579
6624
  let longProduct = 1;
6580
6625
  let shortProduct = 1;
6626
+ const shouldUseAbsoluteQuoteRatio = isSingleAssetSideAgainstStableQuote(longTokens, shortTokens);
6581
6627
  // Long side: PRICE^(WEIGHT/100)
6582
6628
  for (const token of longTokens) {
6583
- const lookup = candleLookups[token.symbol];
6584
- const candle = lookup === null || lookup === void 0 ? void 0 : lookup.get(timestamp);
6629
+ const candle = getCandleOrStableQuote(candleLookups, token.symbol, timestamp);
6585
6630
  if (candle) {
6586
6631
  const price = candle[priceType];
6587
6632
  if (price > 0) {
@@ -6595,13 +6640,14 @@ const calculateWeightedRatio = (longTokens, shortTokens, candleLookups, timestam
6595
6640
  }
6596
6641
  // Short side: PRICE^-(WEIGHT/100)
6597
6642
  for (const token of shortTokens) {
6598
- const lookup = candleLookups[token.symbol];
6599
- const candle = lookup === null || lookup === void 0 ? void 0 : lookup.get(timestamp);
6643
+ const candle = getCandleOrStableQuote(candleLookups, token.symbol, timestamp);
6600
6644
  if (candle) {
6601
6645
  const price = candle[priceType];
6602
6646
  if (price > 0) {
6603
- const weightFactor = token.weight / 100;
6604
- shortProduct *= Math.pow(price, -weightFactor);
6647
+ const weightFactor = shouldUseAbsoluteQuoteRatio
6648
+ ? token.weight / 100
6649
+ : -(token.weight / 100);
6650
+ shortProduct *= Math.pow(price, weightFactor);
6605
6651
  }
6606
6652
  }
6607
6653
  else {
@@ -6654,19 +6700,37 @@ const getCompleteTimestamps = (candleLookups, requiredSymbols) => {
6654
6700
  result.sort((a, b) => a - b);
6655
6701
  return result;
6656
6702
  };
6703
+ const getRequiredSymbols = (longTokens, shortTokens) => [...longTokens, ...shortTokens]
6704
+ .map((token) => token.symbol)
6705
+ .filter((symbol) => !isStableQuoteSymbol(symbol));
6706
+ const getCandleOrStableQuote = (candleLookups, symbol, timestamp) => {
6707
+ var _a, _b;
6708
+ if (isStableQuoteSymbol(symbol)) {
6709
+ return {
6710
+ s: symbol,
6711
+ t: timestamp,
6712
+ T: timestamp,
6713
+ o: 1,
6714
+ h: 1,
6715
+ l: 1,
6716
+ c: 1,
6717
+ };
6718
+ }
6719
+ return (_b = (_a = candleLookups[symbol]) === null || _a === void 0 ? void 0 : _a.get(timestamp)) !== null && _b !== void 0 ? _b : null;
6720
+ };
6657
6721
  /**
6658
6722
  * Compute basket candles from individual token candles using weighted ratios
6659
6723
  * Optimized version that creates lookup maps once and reuses them
6660
6724
  */
6661
6725
  const computeBasketCandles = (longTokens, shortTokens, tokenCandles) => {
6662
- var _a, _b;
6663
6726
  if (longTokens.length === 0 && shortTokens.length === 0) {
6664
6727
  return [];
6665
6728
  }
6666
6729
  // Create efficient lookup maps once
6667
6730
  const candleLookups = createCandleLookups(tokenCandles);
6668
- const allSymbols = [...longTokens, ...shortTokens].map(t => t.symbol);
6731
+ const allSymbols = getRequiredSymbols(longTokens, shortTokens);
6669
6732
  const completeTimestamps = getCompleteTimestamps(candleLookups, allSymbols);
6733
+ const shouldUseAbsoluteQuoteRatio = isSingleAssetSideAgainstStableQuote(longTokens, shortTokens);
6670
6734
  const basketCandles = [];
6671
6735
  for (const timestamp of completeTimestamps) {
6672
6736
  // Compute all OHLC products in a single pass per side
@@ -6676,7 +6740,7 @@ const computeBasketCandles = (longTokens, shortTokens, tokenCandles) => {
6676
6740
  let missing = false;
6677
6741
  // Accumulate volume/trades and compute long side contributions
6678
6742
  for (const token of longTokens) {
6679
- const candle = (_a = candleLookups[token.symbol]) === null || _a === void 0 ? void 0 : _a.get(timestamp);
6743
+ const candle = getCandleOrStableQuote(candleLookups, token.symbol, timestamp);
6680
6744
  if (!candle) {
6681
6745
  missing = true;
6682
6746
  break;
@@ -6701,12 +6765,14 @@ const computeBasketCandles = (longTokens, shortTokens, tokenCandles) => {
6701
6765
  continue;
6702
6766
  // Short side contributions (negative exponent)
6703
6767
  for (const token of shortTokens) {
6704
- const candle = (_b = candleLookups[token.symbol]) === null || _b === void 0 ? void 0 : _b.get(timestamp);
6768
+ const candle = getCandleOrStableQuote(candleLookups, token.symbol, timestamp);
6705
6769
  if (!candle) {
6706
6770
  missing = true;
6707
6771
  break;
6708
6772
  }
6709
- const weightFactor = -(token.weight / 100);
6773
+ const weightFactor = shouldUseAbsoluteQuoteRatio
6774
+ ? token.weight / 100
6775
+ : -(token.weight / 100);
6710
6776
  const o = candle.o;
6711
6777
  const h = candle.h;
6712
6778
  const l = candle.l;
@@ -6780,7 +6846,7 @@ const useBasketCandles = () => {
6780
6846
  const fetchOverallPerformanceCandles = useCallback(async (startTime, endTime, interval) => {
6781
6847
  await fetchHistoricalPriceData(startTime, endTime, interval);
6782
6848
  const allCandles = await getAllHistoricalPriceData();
6783
- const allTokens = [...longTokens, ...shortTokens];
6849
+ const allTokens = [...longTokens, ...shortTokens].filter((token) => !isStableQuoteSymbol(token.symbol));
6784
6850
  if (allTokens.length === 0)
6785
6851
  return [];
6786
6852
  const symbolsData = {};
@@ -6880,6 +6946,8 @@ const useBasketCandles = () => {
6880
6946
  let t = null;
6881
6947
  let T = null;
6882
6948
  for (const symbol of symbolSet) {
6949
+ if (isStableQuoteSymbol(symbol))
6950
+ continue;
6883
6951
  const c = candleData.get(symbol);
6884
6952
  if (!c)
6885
6953
  return null; // missing latest candle for symbol
@@ -6894,6 +6962,17 @@ const useBasketCandles = () => {
6894
6962
  const intervalMs = CANDLE_INTERVAL_MS[candleInterval];
6895
6963
  const maxCarryForwardMs = intervalMs * 2;
6896
6964
  const getCandleForTargetWindow = (symbol) => {
6965
+ if (isStableQuoteSymbol(symbol)) {
6966
+ return {
6967
+ s: symbol,
6968
+ t,
6969
+ T,
6970
+ o: 1,
6971
+ h: 1,
6972
+ l: 1,
6973
+ c: 1,
6974
+ };
6975
+ }
6897
6976
  const c = snapshot[symbol];
6898
6977
  if (c.t === t)
6899
6978
  return c;
@@ -6915,6 +6994,7 @@ const useBasketCandles = () => {
6915
6994
  // Compute weighted OHLC similar to computeBasketCandles
6916
6995
  let longOpen = 1, longHigh = 1, longLow = 1, longClose = 1;
6917
6996
  let shortOpen = 1, shortHigh = 1, shortLow = 1, shortClose = 1;
6997
+ const shouldUseAbsoluteQuoteRatio = isSingleAssetSideAgainstStableQuote(longTokens, shortTokens);
6918
6998
  for (const token of longTokens) {
6919
6999
  const c = getCandleForTargetWindow(token.symbol);
6920
7000
  if (!c)
@@ -6932,7 +7012,9 @@ const useBasketCandles = () => {
6932
7012
  const c = getCandleForTargetWindow(token.symbol);
6933
7013
  if (!c)
6934
7014
  return null;
6935
- const w = -(token.weight / 100);
7015
+ const w = shouldUseAbsoluteQuoteRatio
7016
+ ? token.weight / 100
7017
+ : -(token.weight / 100);
6936
7018
  const o = c.o, h = c.h, l = c.l, cl = c.c;
6937
7019
  if (!(o > 0 && h > 0 && l > 0 && cl > 0))
6938
7020
  return null;
@@ -0,0 +1,6 @@
1
+ import type { TokenSelection } from '../types';
2
+ export declare const STABLE_QUOTE_SYMBOLS: Set<string>;
3
+ export declare const isStableQuoteSymbol: (symbol: string) => boolean;
4
+ export declare const hasNonStableTokens: (tokens: TokenSelection[]) => boolean;
5
+ export declare const isStableOnlySide: (tokens: TokenSelection[]) => boolean;
6
+ export declare const isSingleAssetSideAgainstStableQuote: (longTokens: TokenSelection[], shortTokens: TokenSelection[]) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pear-protocol/hyperliquid-sdk",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "React SDK for Pear Protocol Hyperliquid API integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",