@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 +113 -31
- package/dist/utils/quote-assets.d.ts +6 -0
- package/package.json +1 -1
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]
|
|
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 = (
|
|
1941
|
-
const currentShort = (
|
|
1942
|
-
const prevLong = (
|
|
1943
|
-
const prevShort = (
|
|
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
|
-
|
|
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(
|
|
1994
|
+
longProduct *= Math.pow(price, weightFactor);
|
|
1957
1995
|
}
|
|
1958
1996
|
});
|
|
1959
1997
|
shortTokens.forEach((token) => {
|
|
1960
1998
|
const metadata = shortTokensMetadata[token.symbol];
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
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
|
-
|
|
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(
|
|
2018
|
+
longProduct *= Math.pow(price, weightFactor);
|
|
1977
2019
|
}
|
|
1978
2020
|
});
|
|
1979
2021
|
shortTokens.forEach((token) => {
|
|
1980
2022
|
const metadata = shortTokensMetadata[token.symbol];
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
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
|
|
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
|
|
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 =
|
|
6604
|
-
|
|
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 =
|
|
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 = (
|
|
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 = (
|
|
6768
|
+
const candle = getCandleOrStableQuote(candleLookups, token.symbol, timestamp);
|
|
6705
6769
|
if (!candle) {
|
|
6706
6770
|
missing = true;
|
|
6707
6771
|
break;
|
|
6708
6772
|
}
|
|
6709
|
-
const weightFactor =
|
|
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 =
|
|
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;
|