@pear-protocol/hyperliquid-sdk 0.0.8 → 0.0.10
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/hooks/useCalculatedPositions.d.ts +2 -2
- package/dist/hooks/useTrading.d.ts +20 -8
- package/dist/index.d.ts +70 -111
- package/dist/index.esm.js +294 -348
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +295 -350
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +3 -3
- package/dist/types.d.ts +45 -17
- package/dist/utils/position-processor.d.ts +22 -0
- package/dist/websocket.d.ts +3 -3
- package/package.json +1 -1
- package/dist/utils/aggregate-position-calculator.d.ts +0 -81
package/dist/index.esm.js
CHANGED
|
@@ -2638,21 +2638,17 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2638
2638
|
accountSummary: null,
|
|
2639
2639
|
});
|
|
2640
2640
|
const [lastError, setLastError] = useState(null);
|
|
2641
|
+
const [lastSubscribedAddress, setLastSubscribedAddress] = useState(null);
|
|
2641
2642
|
// WebSocket connection
|
|
2642
|
-
const {
|
|
2643
|
-
{
|
|
2643
|
+
const { readyState, sendMessage } = useWebSocket(wsUrl, {
|
|
2644
2644
|
shouldReconnect: () => true,
|
|
2645
2645
|
reconnectAttempts: 5,
|
|
2646
2646
|
reconnectInterval: 3000,
|
|
2647
|
-
|
|
2648
|
-
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2649
|
-
// Handle incoming WebSocket messages
|
|
2650
|
-
useEffect(() => {
|
|
2651
|
-
if (lastMessage !== null) {
|
|
2647
|
+
onMessage: (event) => {
|
|
2652
2648
|
try {
|
|
2653
|
-
const message = JSON.parse(
|
|
2654
|
-
// Handle subscription responses
|
|
2655
|
-
if ('success' in message || 'error' in message) {
|
|
2649
|
+
const message = JSON.parse(event.data);
|
|
2650
|
+
// Handle subscription responses (only if they don't have channel data)
|
|
2651
|
+
if (('success' in message || 'error' in message) && !('channel' in message)) {
|
|
2656
2652
|
if (message.error) {
|
|
2657
2653
|
setLastError(message.error);
|
|
2658
2654
|
}
|
|
@@ -2664,30 +2660,42 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2664
2660
|
// Handle channel data messages
|
|
2665
2661
|
if ('channel' in message && 'data' in message) {
|
|
2666
2662
|
const dataMessage = message;
|
|
2663
|
+
// Validate data exists and is not null/undefined
|
|
2664
|
+
if (dataMessage.data === null || dataMessage.data === undefined) {
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
2667
|
switch (dataMessage.channel) {
|
|
2668
2668
|
case 'trade-histories':
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2669
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2670
|
+
setData(prev => ({
|
|
2671
|
+
...prev,
|
|
2672
|
+
tradeHistories: dataMessage.data
|
|
2673
|
+
}));
|
|
2674
|
+
}
|
|
2673
2675
|
break;
|
|
2674
2676
|
case 'open-positions':
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2677
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2678
|
+
setData(prev => ({
|
|
2679
|
+
...prev,
|
|
2680
|
+
openPositions: dataMessage.data
|
|
2681
|
+
}));
|
|
2682
|
+
}
|
|
2679
2683
|
break;
|
|
2680
2684
|
case 'open-orders':
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
+
if (Array.isArray(dataMessage.data)) {
|
|
2686
|
+
setData(prev => ({
|
|
2687
|
+
...prev,
|
|
2688
|
+
openOrders: dataMessage.data
|
|
2689
|
+
}));
|
|
2690
|
+
}
|
|
2685
2691
|
break;
|
|
2686
2692
|
case 'account-summary':
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2693
|
+
if (typeof dataMessage.data === 'object' && dataMessage.data !== null) {
|
|
2694
|
+
setData(prev => ({
|
|
2695
|
+
...prev,
|
|
2696
|
+
accountSummary: dataMessage.data
|
|
2697
|
+
}));
|
|
2698
|
+
}
|
|
2691
2699
|
break;
|
|
2692
2700
|
}
|
|
2693
2701
|
}
|
|
@@ -2696,34 +2704,47 @@ const useHyperliquidWebSocket = ({ wsUrl, address }) => {
|
|
|
2696
2704
|
setLastError(`Failed to parse message: ${error instanceof Error ? error.message : String(error)}`);
|
|
2697
2705
|
}
|
|
2698
2706
|
}
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2707
|
+
});
|
|
2708
|
+
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2709
|
+
// Handle subscription management
|
|
2701
2710
|
useEffect(() => {
|
|
2702
|
-
if (isConnected && address) {
|
|
2711
|
+
if (isConnected && address && address !== lastSubscribedAddress) {
|
|
2712
|
+
// Unsubscribe from previous address if exists
|
|
2713
|
+
if (lastSubscribedAddress) {
|
|
2714
|
+
sendMessage(JSON.stringify({
|
|
2715
|
+
action: 'unsubscribe',
|
|
2716
|
+
address: lastSubscribedAddress
|
|
2717
|
+
}));
|
|
2718
|
+
}
|
|
2703
2719
|
// Subscribe to new address
|
|
2704
2720
|
sendMessage(JSON.stringify({
|
|
2705
2721
|
action: 'subscribe',
|
|
2706
2722
|
address: address
|
|
2707
2723
|
}));
|
|
2708
|
-
|
|
2709
|
-
setData({
|
|
2710
|
-
tradeHistories: null,
|
|
2711
|
-
openPositions: null,
|
|
2712
|
-
openOrders: null,
|
|
2713
|
-
accountSummary: null,
|
|
2714
|
-
});
|
|
2724
|
+
setLastSubscribedAddress(address);
|
|
2715
2725
|
setLastError(null);
|
|
2716
2726
|
}
|
|
2717
|
-
else if (isConnected && !address) {
|
|
2718
|
-
//
|
|
2727
|
+
else if (isConnected && !address && lastSubscribedAddress) {
|
|
2728
|
+
// Send unsubscribe action when address is removed
|
|
2729
|
+
sendMessage(JSON.stringify({
|
|
2730
|
+
action: 'unsubscribe',
|
|
2731
|
+
address: lastSubscribedAddress
|
|
2732
|
+
}));
|
|
2733
|
+
setLastSubscribedAddress(null);
|
|
2734
|
+
}
|
|
2735
|
+
}, [isConnected, address, lastSubscribedAddress, sendMessage]);
|
|
2736
|
+
// Clear data when address changes
|
|
2737
|
+
useEffect(() => {
|
|
2738
|
+
if (address !== lastSubscribedAddress) {
|
|
2719
2739
|
setData({
|
|
2720
2740
|
tradeHistories: null,
|
|
2721
2741
|
openPositions: null,
|
|
2722
2742
|
openOrders: null,
|
|
2723
2743
|
accountSummary: null,
|
|
2724
2744
|
});
|
|
2745
|
+
setLastError(null);
|
|
2725
2746
|
}
|
|
2726
|
-
}, [
|
|
2747
|
+
}, [address, lastSubscribedAddress]);
|
|
2727
2748
|
return {
|
|
2728
2749
|
data,
|
|
2729
2750
|
connectionStatus: readyState,
|
|
@@ -2739,29 +2760,61 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2739
2760
|
const [subscribedAddress, setSubscribedAddress] = useState(null);
|
|
2740
2761
|
const pingIntervalRef = useRef(null);
|
|
2741
2762
|
// WebSocket connection to HyperLiquid native API
|
|
2742
|
-
const {
|
|
2763
|
+
const { readyState, sendJsonMessage } = useWebSocket('wss://api.hyperliquid.xyz/ws', {
|
|
2743
2764
|
shouldReconnect: () => true,
|
|
2744
2765
|
reconnectAttempts: 5,
|
|
2745
2766
|
reconnectInterval: 3000,
|
|
2746
|
-
onOpen: () =>
|
|
2747
|
-
onClose: () =>
|
|
2767
|
+
onOpen: () => { },
|
|
2768
|
+
onClose: () => { },
|
|
2748
2769
|
onError: (event) => console.error('[HyperLiquid WS] Connection error:', event),
|
|
2749
2770
|
onReconnectStop: () => console.error('[HyperLiquid WS] Reconnection stopped after 5 attempts'),
|
|
2771
|
+
onMessage: (event) => {
|
|
2772
|
+
try {
|
|
2773
|
+
const message = JSON.parse(event.data);
|
|
2774
|
+
// Handle subscription responses
|
|
2775
|
+
if ('success' in message || 'error' in message) {
|
|
2776
|
+
if (message.error) {
|
|
2777
|
+
console.error('[HyperLiquid WS] Subscription error:', message.error);
|
|
2778
|
+
setLastError(message.error);
|
|
2779
|
+
}
|
|
2780
|
+
else {
|
|
2781
|
+
setLastError(null);
|
|
2782
|
+
}
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
// Handle channel data messages
|
|
2786
|
+
if ('channel' in message && 'data' in message) {
|
|
2787
|
+
const response = message;
|
|
2788
|
+
switch (response.channel) {
|
|
2789
|
+
case 'webData2':
|
|
2790
|
+
setWebData2(response.data);
|
|
2791
|
+
break;
|
|
2792
|
+
case 'allMids':
|
|
2793
|
+
setAllMids(response.data);
|
|
2794
|
+
break;
|
|
2795
|
+
default:
|
|
2796
|
+
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
catch (error) {
|
|
2801
|
+
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
2802
|
+
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', event.data);
|
|
2803
|
+
setLastError(errorMessage);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2750
2806
|
});
|
|
2751
2807
|
const isConnected = readyState === dist.ReadyState.OPEN;
|
|
2752
2808
|
// Setup ping mechanism
|
|
2753
2809
|
useEffect(() => {
|
|
2754
2810
|
if (isConnected) {
|
|
2755
|
-
console.log('[HyperLiquid WS] Setting up ping mechanism (30s interval)');
|
|
2756
2811
|
// Send ping every 30 seconds
|
|
2757
2812
|
pingIntervalRef.current = setInterval(() => {
|
|
2758
|
-
console.log('[HyperLiquid WS] Sending ping');
|
|
2759
2813
|
sendJsonMessage({ method: 'ping' });
|
|
2760
2814
|
}, 30000);
|
|
2761
2815
|
}
|
|
2762
2816
|
else {
|
|
2763
2817
|
if (pingIntervalRef.current) {
|
|
2764
|
-
console.log('[HyperLiquid WS] Clearing ping interval');
|
|
2765
2818
|
clearInterval(pingIntervalRef.current);
|
|
2766
2819
|
pingIntervalRef.current = null;
|
|
2767
2820
|
}
|
|
@@ -2783,7 +2836,6 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2783
2836
|
return;
|
|
2784
2837
|
// Unsubscribe from previous address if exists
|
|
2785
2838
|
if (subscribedAddress) {
|
|
2786
|
-
console.log(`[HyperLiquid WS] Unsubscribing from webData2 for previous address: ${subscribedAddress}`);
|
|
2787
2839
|
const unsubscribeMessage = {
|
|
2788
2840
|
method: 'unsubscribe',
|
|
2789
2841
|
subscription: {
|
|
@@ -2794,7 +2846,6 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2794
2846
|
sendJsonMessage(unsubscribeMessage);
|
|
2795
2847
|
}
|
|
2796
2848
|
// Subscribe to webData2 with new address
|
|
2797
|
-
console.log(`[HyperLiquid WS] Subscribing to webData2 for address: ${userAddress}`);
|
|
2798
2849
|
const subscribeWebData2 = {
|
|
2799
2850
|
method: 'subscribe',
|
|
2800
2851
|
subscription: {
|
|
@@ -2803,7 +2854,6 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2803
2854
|
},
|
|
2804
2855
|
};
|
|
2805
2856
|
// Subscribe to allMids
|
|
2806
|
-
console.log('[HyperLiquid WS] Subscribing to allMids');
|
|
2807
2857
|
const subscribeAllMids = {
|
|
2808
2858
|
method: 'subscribe',
|
|
2809
2859
|
subscription: {
|
|
@@ -2815,53 +2865,9 @@ const useHyperliquidNativeWebSocket = ({ address }) => {
|
|
|
2815
2865
|
setSubscribedAddress(userAddress);
|
|
2816
2866
|
// Clear previous data when address changes
|
|
2817
2867
|
if (subscribedAddress && subscribedAddress !== userAddress) {
|
|
2818
|
-
console.log('[HyperLiquid WS] Address changed, clearing previous webData2');
|
|
2819
2868
|
setWebData2(null);
|
|
2820
2869
|
}
|
|
2821
2870
|
}, [isConnected, address, subscribedAddress, sendJsonMessage]);
|
|
2822
|
-
// Handle incoming WebSocket messages
|
|
2823
|
-
useEffect(() => {
|
|
2824
|
-
if (!lastMessage)
|
|
2825
|
-
return;
|
|
2826
|
-
try {
|
|
2827
|
-
const message = JSON.parse(lastMessage.data);
|
|
2828
|
-
console.log('[HyperLiquid WS] Received message:', message);
|
|
2829
|
-
// Handle subscription responses
|
|
2830
|
-
if ('success' in message || 'error' in message) {
|
|
2831
|
-
if (message.error) {
|
|
2832
|
-
console.error('[HyperLiquid WS] Subscription error:', message.error);
|
|
2833
|
-
setLastError(message.error);
|
|
2834
|
-
}
|
|
2835
|
-
else {
|
|
2836
|
-
console.log('[HyperLiquid WS] Subscription success:', message);
|
|
2837
|
-
setLastError(null);
|
|
2838
|
-
}
|
|
2839
|
-
return;
|
|
2840
|
-
}
|
|
2841
|
-
// Handle channel data messages
|
|
2842
|
-
if ('channel' in message && 'data' in message) {
|
|
2843
|
-
const response = message;
|
|
2844
|
-
console.log(`[HyperLiquid WS] Received ${response.channel} data`);
|
|
2845
|
-
switch (response.channel) {
|
|
2846
|
-
case 'webData2':
|
|
2847
|
-
console.log('[HyperLiquid WS] Setting webData2:', response.data);
|
|
2848
|
-
setWebData2(response.data);
|
|
2849
|
-
break;
|
|
2850
|
-
case 'allMids':
|
|
2851
|
-
console.log('[HyperLiquid WS] Setting allMids:', response.data);
|
|
2852
|
-
setAllMids(response.data);
|
|
2853
|
-
break;
|
|
2854
|
-
default:
|
|
2855
|
-
console.warn(`[HyperLiquid WS] Unknown channel: ${response.channel}`);
|
|
2856
|
-
}
|
|
2857
|
-
}
|
|
2858
|
-
}
|
|
2859
|
-
catch (error) {
|
|
2860
|
-
const errorMessage = `Failed to parse message: ${error instanceof Error ? error.message : String(error)}`;
|
|
2861
|
-
console.error('[HyperLiquid WS] Parse error:', errorMessage, 'Raw message:', lastMessage.data);
|
|
2862
|
-
setLastError(errorMessage);
|
|
2863
|
-
}
|
|
2864
|
-
}, [lastMessage]);
|
|
2865
2871
|
return {
|
|
2866
2872
|
webData2,
|
|
2867
2873
|
allMids,
|
|
@@ -2949,84 +2955,76 @@ const useAddress = () => {
|
|
|
2949
2955
|
};
|
|
2950
2956
|
};
|
|
2951
2957
|
|
|
2952
|
-
/**
|
|
2953
|
-
* Position side enum for calculations
|
|
2954
|
-
*/
|
|
2955
2958
|
var PositionSide;
|
|
2956
2959
|
(function (PositionSide) {
|
|
2957
2960
|
PositionSide["LONG"] = "LONG";
|
|
2958
2961
|
PositionSide["SHORT"] = "SHORT";
|
|
2959
2962
|
})(PositionSide || (PositionSide = {}));
|
|
2960
|
-
|
|
2961
|
-
* Aggregate position calculation utility class that handles cross-position asset syncing
|
|
2962
|
-
*/
|
|
2963
|
-
class AggregatePositionCalculator {
|
|
2963
|
+
class PositionProcessor {
|
|
2964
2964
|
constructor(webData2, allMids) {
|
|
2965
2965
|
this.webData2 = webData2;
|
|
2966
2966
|
this.allMids = allMids;
|
|
2967
2967
|
}
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2968
|
+
execute(rawPositions) {
|
|
2969
|
+
if (!rawPositions || rawPositions.length === 0) {
|
|
2970
|
+
return [];
|
|
2971
|
+
}
|
|
2972
|
+
const userHyperliquidPositions = this.getUserPositions();
|
|
2973
|
+
const platformTotalsByAsset = this.calculatePlatformTotalsByAsset(rawPositions);
|
|
2974
|
+
const hlPositionsMap = new Map();
|
|
2975
|
+
(userHyperliquidPositions || []).forEach(assetPos => {
|
|
2976
|
+
var _a;
|
|
2977
|
+
if ((_a = assetPos.position) === null || _a === void 0 ? void 0 : _a.coin) {
|
|
2978
|
+
hlPositionsMap.set(assetPos.position.coin, assetPos);
|
|
2979
|
+
}
|
|
2980
|
+
});
|
|
2981
|
+
const openPositionDtos = [];
|
|
2982
|
+
for (const position of rawPositions) {
|
|
2983
|
+
const syncedPositionDto = this.syncPositionWithAggregateData(position, hlPositionsMap, platformTotalsByAsset);
|
|
2984
|
+
openPositionDtos.push(syncedPositionDto);
|
|
2985
|
+
}
|
|
2986
|
+
return openPositionDtos;
|
|
2987
|
+
}
|
|
2988
|
+
getUserPositions() {
|
|
2989
|
+
var _a, _b;
|
|
2990
|
+
return ((_b = (_a = this.webData2) === null || _a === void 0 ? void 0 : _a.clearinghouseState) === null || _b === void 0 ? void 0 : _b.assetPositions) || [];
|
|
2991
|
+
}
|
|
2971
2992
|
getMarketPrice(coin) {
|
|
2972
2993
|
var _a;
|
|
2973
2994
|
if (!((_a = this.allMids) === null || _a === void 0 ? void 0 : _a.mids))
|
|
2974
2995
|
return 0;
|
|
2975
|
-
// Try exact match first
|
|
2976
2996
|
const exactPrice = this.allMids.mids[coin];
|
|
2977
2997
|
if (exactPrice) {
|
|
2978
2998
|
return Number(exactPrice);
|
|
2979
2999
|
}
|
|
2980
|
-
|
|
2981
|
-
const baseCurrency = coin.split('/')[0];
|
|
3000
|
+
const baseCurrency = this.extractBaseCurrency(coin);
|
|
2982
3001
|
const basePrice = this.allMids.mids[baseCurrency];
|
|
2983
3002
|
if (basePrice) {
|
|
2984
3003
|
return Number(basePrice);
|
|
2985
3004
|
}
|
|
2986
|
-
console.warn(`[AggregatePositionCalculator] No market price found for coin: ${coin}`);
|
|
2987
3005
|
return 0;
|
|
2988
3006
|
}
|
|
2989
|
-
/**
|
|
2990
|
-
* Get user positions from webData2
|
|
2991
|
-
*/
|
|
2992
|
-
getUserPositions() {
|
|
2993
|
-
var _a, _b;
|
|
2994
|
-
return ((_b = (_a = this.webData2) === null || _a === void 0 ? void 0 : _a.clearinghouseState) === null || _b === void 0 ? void 0 : _b.assetPositions) || [];
|
|
2995
|
-
}
|
|
2996
|
-
/**
|
|
2997
|
-
* Calculate updated open positions by syncing platform positions with HyperLiquid data
|
|
2998
|
-
* Uses aggregate totals across all positions for accurate cross-position sync
|
|
2999
|
-
*/
|
|
3000
|
-
calculateOpenPositions(platformPositions) {
|
|
3001
|
-
if (!(platformPositions === null || platformPositions === void 0 ? void 0 : platformPositions.length)) {
|
|
3002
|
-
return [];
|
|
3003
|
-
}
|
|
3004
|
-
const hyperliquidPositions = this.getUserPositions();
|
|
3005
|
-
// Build a map of total platform sizes per asset across ALL positions
|
|
3006
|
-
const platformTotalsByAsset = this.calculatePlatformTotalsByAsset(platformPositions);
|
|
3007
|
-
// Create a map of HyperLiquid positions by coin
|
|
3008
|
-
const hlPositionsMap = new Map();
|
|
3009
|
-
hyperliquidPositions.forEach(assetPos => {
|
|
3010
|
-
var _a;
|
|
3011
|
-
if ((_a = assetPos.position) === null || _a === void 0 ? void 0 : _a.coin) {
|
|
3012
|
-
hlPositionsMap.set(assetPos.position.coin, assetPos);
|
|
3013
|
-
}
|
|
3014
|
-
});
|
|
3015
|
-
// Process each position with awareness of the aggregated totals
|
|
3016
|
-
return platformPositions.map(position => this.syncPositionWithAggregateData(position, hlPositionsMap, platformTotalsByAsset));
|
|
3017
|
-
}
|
|
3018
|
-
/**
|
|
3019
|
-
* Calculate total platform sizes per asset across all positions
|
|
3020
|
-
*/
|
|
3021
3007
|
calculatePlatformTotalsByAsset(positions) {
|
|
3022
3008
|
const totalsMap = new Map();
|
|
3023
3009
|
for (const position of positions) {
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3010
|
+
for (const asset of position.longAssets || []) {
|
|
3011
|
+
const baseCurrency = this.extractBaseCurrency(asset.coin);
|
|
3012
|
+
if (!totalsMap.has(baseCurrency)) {
|
|
3013
|
+
totalsMap.set(baseCurrency, {
|
|
3014
|
+
totalSize: 0,
|
|
3015
|
+
positions: []
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
const totals = totalsMap.get(baseCurrency);
|
|
3019
|
+
const assetSize = Number(asset.size || 0);
|
|
3020
|
+
totals.totalSize += assetSize;
|
|
3021
|
+
totals.positions.push({
|
|
3022
|
+
positionId: position.positionId,
|
|
3023
|
+
asset: asset,
|
|
3024
|
+
size: assetSize
|
|
3025
|
+
});
|
|
3026
|
+
}
|
|
3027
|
+
for (const asset of position.shortAssets || []) {
|
|
3030
3028
|
const baseCurrency = this.extractBaseCurrency(asset.coin);
|
|
3031
3029
|
if (!totalsMap.has(baseCurrency)) {
|
|
3032
3030
|
totalsMap.set(baseCurrency, {
|
|
@@ -3035,7 +3033,7 @@ class AggregatePositionCalculator {
|
|
|
3035
3033
|
});
|
|
3036
3034
|
}
|
|
3037
3035
|
const totals = totalsMap.get(baseCurrency);
|
|
3038
|
-
const assetSize = asset.
|
|
3036
|
+
const assetSize = Number(asset.size || 0);
|
|
3039
3037
|
totals.totalSize += assetSize;
|
|
3040
3038
|
totals.positions.push({
|
|
3041
3039
|
positionId: position.positionId,
|
|
@@ -3046,47 +3044,44 @@ class AggregatePositionCalculator {
|
|
|
3046
3044
|
}
|
|
3047
3045
|
return totalsMap;
|
|
3048
3046
|
}
|
|
3049
|
-
/**
|
|
3050
|
-
* Extract base currency from asset name (handles "LINK/USD" -> "LINK")
|
|
3051
|
-
*/
|
|
3052
3047
|
extractBaseCurrency(assetName) {
|
|
3053
3048
|
return assetName.split('/')[0] || assetName;
|
|
3054
3049
|
}
|
|
3055
|
-
/**
|
|
3056
|
-
* Sync a single position with HyperLiquid data using aggregate totals
|
|
3057
|
-
*/
|
|
3058
3050
|
syncPositionWithAggregateData(position, hlPositionsMap, platformTotalsByAsset) {
|
|
3059
3051
|
const syncResults = [];
|
|
3060
3052
|
let hasExternalModification = false;
|
|
3061
3053
|
let allAssetsClosed = true;
|
|
3062
|
-
// Separate tracking for long and short sides
|
|
3063
3054
|
let longAssetStatuses = { total: 0, closed: 0 };
|
|
3064
3055
|
let shortAssetStatuses = { total: 0, closed: 0 };
|
|
3065
|
-
// Process
|
|
3066
|
-
const
|
|
3067
|
-
...position.longAssets.map(asset => ({ ...asset, side: PositionSide.LONG })),
|
|
3068
|
-
...position.shortAssets.map(asset => ({ ...asset, side: PositionSide.SHORT }))
|
|
3069
|
-
];
|
|
3070
|
-
for (const asset of allAssets) {
|
|
3056
|
+
// Process long assets
|
|
3057
|
+
for (const asset of position.longAssets || []) {
|
|
3071
3058
|
const baseCurrency = this.extractBaseCurrency(asset.coin);
|
|
3072
3059
|
const hlPosition = hlPositionsMap.get(baseCurrency);
|
|
3073
3060
|
const platformTotals = platformTotalsByAsset.get(baseCurrency);
|
|
3074
|
-
const syncResult = this.syncAssetWithAggregateData(asset, hlPosition, (platformTotals === null || platformTotals === void 0 ? void 0 : platformTotals.totalSize) || 0);
|
|
3061
|
+
const syncResult = this.syncAssetWithAggregateData({ ...asset, side: PositionSide.LONG }, hlPosition, (platformTotals === null || platformTotals === void 0 ? void 0 : platformTotals.totalSize) || 0);
|
|
3075
3062
|
syncResults.push(syncResult);
|
|
3076
|
-
|
|
3077
|
-
if (
|
|
3078
|
-
longAssetStatuses.
|
|
3079
|
-
if (syncResult.actualSize === 0) {
|
|
3080
|
-
longAssetStatuses.closed++;
|
|
3081
|
-
}
|
|
3063
|
+
longAssetStatuses.total++;
|
|
3064
|
+
if (syncResult.actualSize === 0) {
|
|
3065
|
+
longAssetStatuses.closed++;
|
|
3082
3066
|
}
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3067
|
+
if (syncResult.isExternallyModified) {
|
|
3068
|
+
hasExternalModification = true;
|
|
3069
|
+
}
|
|
3070
|
+
if (syncResult.actualSize !== 0) {
|
|
3071
|
+
allAssetsClosed = false;
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
// Process short assets
|
|
3075
|
+
for (const asset of position.shortAssets || []) {
|
|
3076
|
+
const baseCurrency = this.extractBaseCurrency(asset.coin);
|
|
3077
|
+
const hlPosition = hlPositionsMap.get(baseCurrency);
|
|
3078
|
+
const platformTotals = platformTotalsByAsset.get(baseCurrency);
|
|
3079
|
+
const syncResult = this.syncAssetWithAggregateData({ ...asset, side: PositionSide.SHORT }, hlPosition, (platformTotals === null || platformTotals === void 0 ? void 0 : platformTotals.totalSize) || 0);
|
|
3080
|
+
syncResults.push(syncResult);
|
|
3081
|
+
shortAssetStatuses.total++;
|
|
3082
|
+
if (syncResult.actualSize === 0) {
|
|
3083
|
+
shortAssetStatuses.closed++;
|
|
3088
3084
|
}
|
|
3089
|
-
// Update flags
|
|
3090
3085
|
if (syncResult.isExternallyModified) {
|
|
3091
3086
|
hasExternalModification = true;
|
|
3092
3087
|
}
|
|
@@ -3094,17 +3089,27 @@ class AggregatePositionCalculator {
|
|
|
3094
3089
|
allAssetsClosed = false;
|
|
3095
3090
|
}
|
|
3096
3091
|
}
|
|
3097
|
-
// Determine sync status
|
|
3098
3092
|
const syncStatus = this.determineSyncStatus(hasExternalModification, allAssetsClosed, longAssetStatuses, shortAssetStatuses);
|
|
3099
|
-
return this.
|
|
3093
|
+
return this.mapPositionToDtoWithSyncData(position, syncResults, syncStatus);
|
|
3094
|
+
}
|
|
3095
|
+
determineSyncStatus(hasExternalModification, allAssetsClosed, longAssetStatuses, shortAssetStatuses) {
|
|
3096
|
+
if (allAssetsClosed) {
|
|
3097
|
+
return 'EXTERNALLY_CLOSED';
|
|
3098
|
+
}
|
|
3099
|
+
const allLongsClosed = longAssetStatuses.total > 0 &&
|
|
3100
|
+
longAssetStatuses.closed === longAssetStatuses.total;
|
|
3101
|
+
const allShortsClosed = shortAssetStatuses.total > 0 &&
|
|
3102
|
+
shortAssetStatuses.closed === shortAssetStatuses.total;
|
|
3103
|
+
if ((allLongsClosed && !allShortsClosed) || (!allLongsClosed && allShortsClosed)) {
|
|
3104
|
+
return 'PAIR_BROKEN';
|
|
3105
|
+
}
|
|
3106
|
+
if (hasExternalModification) {
|
|
3107
|
+
return 'EXTERNALLY_MODIFIED';
|
|
3108
|
+
}
|
|
3109
|
+
return 'SYNCED';
|
|
3100
3110
|
}
|
|
3101
|
-
/**
|
|
3102
|
-
* Sync individual asset with aggregate data awareness
|
|
3103
|
-
*/
|
|
3104
3111
|
syncAssetWithAggregateData(asset, hlPosition, platformTotal) {
|
|
3105
|
-
|
|
3106
|
-
const platformSize = asset.platformSize;
|
|
3107
|
-
// No position on HyperLiquid - asset was closed
|
|
3112
|
+
const platformSize = Number(asset.size || 0);
|
|
3108
3113
|
if (!hlPosition || !hlPosition.position || !hlPosition.position.szi) {
|
|
3109
3114
|
return {
|
|
3110
3115
|
asset,
|
|
@@ -3112,27 +3117,23 @@ class AggregatePositionCalculator {
|
|
|
3112
3117
|
isExternallyModified: true,
|
|
3113
3118
|
cumFunding: { allTime: 0, sinceChange: 0, sinceOpen: 0 },
|
|
3114
3119
|
unrealizedPnl: 0,
|
|
3115
|
-
liquidationPrice: 0
|
|
3116
|
-
side: asset.side
|
|
3120
|
+
liquidationPrice: 0
|
|
3117
3121
|
};
|
|
3118
3122
|
}
|
|
3119
3123
|
const hlTotalSize = Math.abs(Number(hlPosition.position.szi || 0));
|
|
3120
|
-
// Check if the TOTAL platform size matches HyperLiquid total
|
|
3121
3124
|
const totalDifference = Math.abs(hlTotalSize - platformTotal);
|
|
3122
|
-
const tolerance = platformTotal * 0.001;
|
|
3125
|
+
const tolerance = platformTotal * 0.001;
|
|
3123
3126
|
const isExternallyModified = totalDifference > tolerance;
|
|
3124
|
-
// Calculate this position's proportional share of the HyperLiquid position
|
|
3125
3127
|
const proportion = platformTotal > 0 ? platformSize / platformTotal : 0;
|
|
3126
3128
|
const actualSize = hlTotalSize * proportion;
|
|
3127
|
-
//
|
|
3129
|
+
// Get cumFunding from hlPosition.position.cumFunding
|
|
3130
|
+
const rawCumFunding = hlPosition.position.cumFunding;
|
|
3128
3131
|
const cumFunding = {
|
|
3129
|
-
allTime: Number((
|
|
3130
|
-
sinceChange: Number((
|
|
3131
|
-
sinceOpen: Number((
|
|
3132
|
+
allTime: Number((rawCumFunding === null || rawCumFunding === void 0 ? void 0 : rawCumFunding.allTime) || 0),
|
|
3133
|
+
sinceChange: Number((rawCumFunding === null || rawCumFunding === void 0 ? void 0 : rawCumFunding.sinceChange) || 0) * proportion,
|
|
3134
|
+
sinceOpen: Number((rawCumFunding === null || rawCumFunding === void 0 ? void 0 : rawCumFunding.sinceOpen) || 0) * proportion
|
|
3132
3135
|
};
|
|
3133
|
-
// Distribute PnL proportionally
|
|
3134
3136
|
const unrealizedPnl = Number(hlPosition.position.unrealizedPnl || 0) * proportion;
|
|
3135
|
-
// Liquidation price is the same for all positions of the same asset
|
|
3136
3137
|
const liquidationPrice = Number(hlPosition.position.liquidationPx || 0);
|
|
3137
3138
|
return {
|
|
3138
3139
|
asset,
|
|
@@ -3140,46 +3141,27 @@ class AggregatePositionCalculator {
|
|
|
3140
3141
|
isExternallyModified,
|
|
3141
3142
|
cumFunding,
|
|
3142
3143
|
unrealizedPnl,
|
|
3143
|
-
liquidationPrice
|
|
3144
|
-
side: asset.side
|
|
3144
|
+
liquidationPrice
|
|
3145
3145
|
};
|
|
3146
3146
|
}
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
}
|
|
3155
|
-
// Check if pair is broken (one entire side closed)
|
|
3156
|
-
const allLongsClosed = longAssetStatuses.total > 0 &&
|
|
3157
|
-
longAssetStatuses.closed === longAssetStatuses.total;
|
|
3158
|
-
const allShortsClosed = shortAssetStatuses.total > 0 &&
|
|
3159
|
-
shortAssetStatuses.closed === shortAssetStatuses.total;
|
|
3160
|
-
if ((allLongsClosed && !allShortsClosed) || (!allLongsClosed && allShortsClosed)) {
|
|
3161
|
-
return 'PAIR_BROKEN';
|
|
3162
|
-
}
|
|
3163
|
-
// External modification detected
|
|
3164
|
-
if (hasExternalModification) {
|
|
3165
|
-
return 'EXTERNALLY_MODIFIED';
|
|
3166
|
-
}
|
|
3167
|
-
// Everything synced properly
|
|
3168
|
-
return 'SYNCED';
|
|
3169
|
-
}
|
|
3170
|
-
/**
|
|
3171
|
-
* Build updated position with synced data
|
|
3172
|
-
*/
|
|
3173
|
-
buildUpdatedPosition(originalPosition, syncResults, syncStatus) {
|
|
3174
|
-
const longSyncResults = syncResults.filter(result => result.side === PositionSide.LONG);
|
|
3175
|
-
const shortSyncResults = syncResults.filter(result => result.side === PositionSide.SHORT);
|
|
3176
|
-
// Calculate position metrics
|
|
3147
|
+
mapPositionToDtoWithSyncData(position, syncResults, syncStatus) {
|
|
3148
|
+
var _a, _b;
|
|
3149
|
+
const longAssets = ((_a = position.longAssets) === null || _a === void 0 ? void 0 : _a.filter(asset => asset)) || [];
|
|
3150
|
+
const shortAssets = ((_b = position.shortAssets) === null || _b === void 0 ? void 0 : _b.filter(asset => asset)) || [];
|
|
3151
|
+
const syncResultsMap = new Map();
|
|
3152
|
+
syncResults.forEach(result => {
|
|
3153
|
+
syncResultsMap.set(`${result.asset.coin}-${result.asset.side}`, result);
|
|
3154
|
+
});
|
|
3177
3155
|
const currentTotalPositionValue = this.calculateCurrentTotalPositionValue(syncResults);
|
|
3178
3156
|
const entryTotalPositionValue = this.calculateEntryTotalPositionValue(syncResults);
|
|
3179
|
-
const totalMarginUsed = entryTotalPositionValue /
|
|
3157
|
+
const totalMarginUsed = entryTotalPositionValue / position.leverage;
|
|
3180
3158
|
return {
|
|
3181
|
-
|
|
3182
|
-
|
|
3159
|
+
syncStatus: syncStatus,
|
|
3160
|
+
positionId: position.positionId,
|
|
3161
|
+
address: position.address,
|
|
3162
|
+
leverage: position.leverage,
|
|
3163
|
+
stopLoss: position.stopLoss,
|
|
3164
|
+
takeProfit: position.takeProfit,
|
|
3183
3165
|
entryRatio: this.calculateEntryRatio(syncResults),
|
|
3184
3166
|
markRatio: this.calculateMarkRatio(syncResults),
|
|
3185
3167
|
netFunding: this.calculateNetFundingFromSyncResults(syncResults),
|
|
@@ -3187,90 +3169,73 @@ class AggregatePositionCalculator {
|
|
|
3187
3169
|
marginUsed: totalMarginUsed,
|
|
3188
3170
|
unrealizedPnl: this.calculateTotalUnrealizedPnlFromSyncResults(syncResults),
|
|
3189
3171
|
lastSyncAt: new Date().toISOString(),
|
|
3190
|
-
longAssets:
|
|
3191
|
-
shortAssets:
|
|
3192
|
-
|
|
3172
|
+
longAssets: longAssets.map(asset => this.mapAssetToDetailDto(asset, syncResultsMap.get(`${asset.coin}-LONG`))),
|
|
3173
|
+
shortAssets: shortAssets.map(asset => this.mapAssetToDetailDto(asset, syncResultsMap.get(`${asset.coin}-SHORT`))),
|
|
3174
|
+
createdAt: position.createdAt,
|
|
3175
|
+
updatedAt: position.updatedAt
|
|
3193
3176
|
};
|
|
3194
3177
|
}
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
const
|
|
3200
|
-
const positionValue = syncResult.actualSize * currentPrice;
|
|
3201
|
-
const entryValue = syncResult.actualSize * syncResult.asset.entryPrice;
|
|
3178
|
+
mapAssetToDetailDto(asset, syncData) {
|
|
3179
|
+
const currentPrice = this.getMarketPrice(asset.coin);
|
|
3180
|
+
const actualSize = (syncData === null || syncData === void 0 ? void 0 : syncData.actualSize) || Number(asset.size || 0);
|
|
3181
|
+
const positionValue = actualSize * currentPrice;
|
|
3182
|
+
const entryValue = Number(asset.size || 0) * Number(asset.entryPrice || 0);
|
|
3202
3183
|
return {
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3184
|
+
coin: asset.coin,
|
|
3185
|
+
entryPrice: Number(asset.entryPrice || 0),
|
|
3186
|
+
platformSize: Number(asset.size || 0),
|
|
3187
|
+
actualSize: actualSize,
|
|
3188
|
+
isExternallyModified: (syncData === null || syncData === void 0 ? void 0 : syncData.isExternallyModified) || false,
|
|
3189
|
+
cumFunding: (syncData === null || syncData === void 0 ? void 0 : syncData.cumFunding) || {
|
|
3190
|
+
allTime: 0,
|
|
3191
|
+
sinceChange: 0,
|
|
3192
|
+
sinceOpen: 0
|
|
3193
|
+
},
|
|
3207
3194
|
marginUsed: entryValue,
|
|
3208
3195
|
positionValue: positionValue,
|
|
3209
|
-
unrealizedPnl:
|
|
3210
|
-
liquidationPrice:
|
|
3196
|
+
unrealizedPnl: (syncData === null || syncData === void 0 ? void 0 : syncData.unrealizedPnl) || 0,
|
|
3197
|
+
liquidationPrice: (syncData === null || syncData === void 0 ? void 0 : syncData.liquidationPrice) || 0
|
|
3211
3198
|
};
|
|
3212
3199
|
}
|
|
3213
|
-
/**
|
|
3214
|
-
* Calculate entry ratio using actual sizes from sync results
|
|
3215
|
-
*/
|
|
3216
3200
|
calculateEntryRatio(syncResults) {
|
|
3217
|
-
|
|
3218
|
-
const
|
|
3201
|
+
var _a, _b;
|
|
3202
|
+
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3203
|
+
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3219
3204
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3220
3205
|
return 0;
|
|
3221
|
-
const
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
const shortValue = shortResults.reduce((sum, result) => {
|
|
3225
|
-
return sum + (result.actualSize * result.asset.entryPrice);
|
|
3226
|
-
}, 0);
|
|
3227
|
-
return shortValue > 0 ? longValue / shortValue : 0;
|
|
3206
|
+
const longEntryPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.entryPrice) ? Number(longResults[0].asset.entryPrice) : 0;
|
|
3207
|
+
const shortEntryPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.entryPrice) ? Number(shortResults[0].asset.entryPrice) : 0;
|
|
3208
|
+
return shortEntryPrice > 0 ? longEntryPrice / shortEntryPrice : 0;
|
|
3228
3209
|
}
|
|
3229
|
-
/**
|
|
3230
|
-
* Calculate mark ratio using actual sizes and current prices
|
|
3231
|
-
*/
|
|
3232
3210
|
calculateMarkRatio(syncResults) {
|
|
3233
|
-
|
|
3234
|
-
const
|
|
3211
|
+
var _a, _b;
|
|
3212
|
+
const longResults = syncResults.filter(result => result.asset.side === PositionSide.LONG);
|
|
3213
|
+
const shortResults = syncResults.filter(result => result.asset.side === PositionSide.SHORT);
|
|
3235
3214
|
if (longResults.length === 0 || shortResults.length === 0)
|
|
3236
3215
|
return 0;
|
|
3237
|
-
const
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
}, 0);
|
|
3241
|
-
const shortValue = shortResults.reduce((sum, result) => {
|
|
3242
|
-
const currentPrice = this.getMarketPrice(result.asset.coin);
|
|
3243
|
-
return sum + (result.actualSize * currentPrice);
|
|
3244
|
-
}, 0);
|
|
3245
|
-
return shortValue > 0 ? longValue / shortValue : 0;
|
|
3216
|
+
const longMarkPrice = ((_a = longResults[0]) === null || _a === void 0 ? void 0 : _a.asset.coin) ? this.getMarketPrice(longResults[0].asset.coin) : 0;
|
|
3217
|
+
const shortMarkPrice = ((_b = shortResults[0]) === null || _b === void 0 ? void 0 : _b.asset.coin) ? this.getMarketPrice(shortResults[0].asset.coin) : 0;
|
|
3218
|
+
return shortMarkPrice > 0 ? longMarkPrice / shortMarkPrice : 0;
|
|
3246
3219
|
}
|
|
3247
|
-
/**
|
|
3248
|
-
* Calculate net funding from sync results
|
|
3249
|
-
*/
|
|
3250
3220
|
calculateNetFundingFromSyncResults(syncResults) {
|
|
3251
|
-
|
|
3221
|
+
const netFunding = syncResults.reduce((sum, result) => {
|
|
3222
|
+
const funding = result.cumFunding.sinceOpen;
|
|
3223
|
+
return sum + funding;
|
|
3224
|
+
}, 0);
|
|
3225
|
+
return netFunding;
|
|
3252
3226
|
}
|
|
3253
|
-
/**
|
|
3254
|
-
* Calculate total unrealized PnL from sync results
|
|
3255
|
-
*/
|
|
3256
3227
|
calculateTotalUnrealizedPnlFromSyncResults(syncResults) {
|
|
3257
3228
|
return syncResults.reduce((sum, result) => sum + result.unrealizedPnl, 0);
|
|
3258
3229
|
}
|
|
3259
|
-
/**
|
|
3260
|
-
* Calculate current total position value using market prices
|
|
3261
|
-
*/
|
|
3262
3230
|
calculateCurrentTotalPositionValue(syncResults) {
|
|
3263
3231
|
return syncResults.reduce((sum, result) => {
|
|
3264
3232
|
const currentPrice = this.getMarketPrice(result.asset.coin);
|
|
3265
3233
|
return sum + (result.actualSize * currentPrice);
|
|
3266
3234
|
}, 0);
|
|
3267
3235
|
}
|
|
3268
|
-
/**
|
|
3269
|
-
* Calculate entry total position value using entry prices
|
|
3270
|
-
*/
|
|
3271
3236
|
calculateEntryTotalPositionValue(syncResults) {
|
|
3272
3237
|
return syncResults.reduce((sum, result) => {
|
|
3273
|
-
return sum + (result.
|
|
3238
|
+
return sum + (Number(result.asset.size || 0) * Number(result.asset.entryPrice || 0));
|
|
3274
3239
|
}, 0);
|
|
3275
3240
|
}
|
|
3276
3241
|
}
|
|
@@ -3280,32 +3245,18 @@ class AggregatePositionCalculator {
|
|
|
3280
3245
|
*/
|
|
3281
3246
|
const useCalculatedOpenPositions = (platformPositions, webData2, allMids) => {
|
|
3282
3247
|
const calculatedPositions = useMemo(() => {
|
|
3283
|
-
var _a, _b;
|
|
3284
3248
|
// Return null if we don't have platform positions yet
|
|
3285
3249
|
if (!platformPositions) {
|
|
3286
|
-
console.log('[useCalculatedOpenPositions] No platform positions available');
|
|
3287
3250
|
return null;
|
|
3288
3251
|
}
|
|
3289
|
-
// If we don't have real-time data yet,
|
|
3252
|
+
// If we don't have real-time data yet, we can't calculate properly, return null
|
|
3290
3253
|
if (!webData2 || !allMids) {
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
hasAllMids: !!allMids,
|
|
3298
|
-
hyperliquidPositionsCount: ((_b = (_a = webData2.clearinghouseState) === null || _a === void 0 ? void 0 : _a.assetPositions) === null || _b === void 0 ? void 0 : _b.length) || 0,
|
|
3299
|
-
availableMids: Object.keys(allMids.mids || {}).length
|
|
3300
|
-
});
|
|
3301
|
-
// Create calculator and compute positions
|
|
3302
|
-
const calculator = new AggregatePositionCalculator(webData2, allMids);
|
|
3303
|
-
const calculated = calculator.calculateOpenPositions(platformPositions);
|
|
3304
|
-
console.log('[useCalculatedOpenPositions] Calculation completed', {
|
|
3305
|
-
inputCount: platformPositions.length,
|
|
3306
|
-
outputCount: calculated.length
|
|
3307
|
-
});
|
|
3308
|
-
return calculated;
|
|
3254
|
+
return null;
|
|
3255
|
+
}
|
|
3256
|
+
// Create processor and compute positions
|
|
3257
|
+
const processor = new PositionProcessor(webData2, allMids);
|
|
3258
|
+
const processed = processor.execute(platformPositions);
|
|
3259
|
+
return processed;
|
|
3309
3260
|
}, [platformPositions, webData2, allMids]);
|
|
3310
3261
|
return calculatedPositions;
|
|
3311
3262
|
};
|
|
@@ -3324,19 +3275,11 @@ class AccountSummaryCalculator {
|
|
|
3324
3275
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
3325
3276
|
// If we don't have webData2, return platform data as-is
|
|
3326
3277
|
if (!((_a = this.webData2) === null || _a === void 0 ? void 0 : _a.clearinghouseState)) {
|
|
3327
|
-
console.log('[AccountSummaryCalculator] No webData2 clearinghouseState available, returning platform data');
|
|
3328
3278
|
return platformAccountSummary;
|
|
3329
3279
|
}
|
|
3330
|
-
console.log('[AccountSummaryCalculator] Calculating account summary with real-time data', {
|
|
3331
|
-
hasPlatformSummary: !!platformAccountSummary,
|
|
3332
|
-
hasOpenOrders: !!platformOpenOrders,
|
|
3333
|
-
openOrdersCount: (platformOpenOrders === null || platformOpenOrders === void 0 ? void 0 : platformOpenOrders.length) || 0,
|
|
3334
|
-
hasAgentWallet: !!agentWalletAddress
|
|
3335
|
-
});
|
|
3336
3280
|
const clearinghouseState = this.webData2.clearinghouseState;
|
|
3337
3281
|
// Calculate total limit order value from platform orders
|
|
3338
3282
|
const totalLimitOrderValue = this.calculateTotalLimitOrderValue(platformOpenOrders || []);
|
|
3339
|
-
console.log('[AccountSummaryCalculator] Total limit order value:', totalLimitOrderValue);
|
|
3340
3283
|
// Use real-time data from webData2 clearinghouseState
|
|
3341
3284
|
const withdrawableAmount = parseFloat(clearinghouseState.withdrawable || '0');
|
|
3342
3285
|
const adjustedWithdrawable = Math.max(0, withdrawableAmount - totalLimitOrderValue);
|
|
@@ -3363,12 +3306,6 @@ class AccountSummaryCalculator {
|
|
|
3363
3306
|
status: agentWalletStatus || ((_l = platformAccountSummary === null || platformAccountSummary === void 0 ? void 0 : platformAccountSummary.agentWallet) === null || _l === void 0 ? void 0 : _l.status) || 'UNKNOWN'
|
|
3364
3307
|
}
|
|
3365
3308
|
};
|
|
3366
|
-
console.log('[AccountSummaryCalculator] Calculated account summary:', {
|
|
3367
|
-
accountValue: accountSummary.balanceSummary.crossMarginSummary.accountValue,
|
|
3368
|
-
withdrawable: accountSummary.balanceSummary.withdrawable,
|
|
3369
|
-
totalMarginUsed: accountSummary.balanceSummary.crossMarginSummary.totalMarginUsed,
|
|
3370
|
-
agentWalletAddress: accountSummary.agentWallet.address
|
|
3371
|
-
});
|
|
3372
3309
|
return accountSummary;
|
|
3373
3310
|
}
|
|
3374
3311
|
/**
|
|
@@ -3381,11 +3318,6 @@ class AccountSummaryCalculator {
|
|
|
3381
3318
|
const totalValue = openOrders
|
|
3382
3319
|
.filter(order => order.status === 'OPEN' || order.status === 'PROCESSING')
|
|
3383
3320
|
.reduce((sum, order) => sum + order.usdValue, 0);
|
|
3384
|
-
console.log('[AccountSummaryCalculator] Calculated limit order value:', {
|
|
3385
|
-
totalOrders: openOrders.length,
|
|
3386
|
-
openOrders: openOrders.filter(order => order.status === 'OPEN' || order.status === 'PROCESSING').length,
|
|
3387
|
-
totalValue
|
|
3388
|
-
});
|
|
3389
3321
|
return totalValue;
|
|
3390
3322
|
}
|
|
3391
3323
|
/**
|
|
@@ -3409,52 +3341,41 @@ class AccountSummaryCalculator {
|
|
|
3409
3341
|
*/
|
|
3410
3342
|
const useCalculatedAccountSummary = (platformAccountSummary, platformOpenOrders, webData2, agentWalletAddress, agentWalletStatus) => {
|
|
3411
3343
|
const calculatedAccountSummary = useMemo(() => {
|
|
3412
|
-
var _a, _b, _c;
|
|
3413
3344
|
// Return null if we don't have platform account summary yet
|
|
3414
3345
|
if (!platformAccountSummary) {
|
|
3415
|
-
console.log('[useCalculatedAccountSummary] No platform account summary available');
|
|
3416
3346
|
return null;
|
|
3417
3347
|
}
|
|
3418
3348
|
// If we don't have real-time data yet, return platform summary as-is
|
|
3419
3349
|
if (!(webData2 === null || webData2 === void 0 ? void 0 : webData2.clearinghouseState)) {
|
|
3420
|
-
console.log('[useCalculatedAccountSummary] Missing webData2 clearinghouseState, returning platform summary');
|
|
3421
3350
|
return platformAccountSummary;
|
|
3422
3351
|
}
|
|
3423
|
-
console.log('[useCalculatedAccountSummary] Calculating account summary with real-time data', {
|
|
3424
|
-
hasPlatformSummary: !!platformAccountSummary,
|
|
3425
|
-
hasWebData2: !!webData2,
|
|
3426
|
-
hasClearinghouseState: !!webData2.clearinghouseState,
|
|
3427
|
-
hasOpenOrders: !!platformOpenOrders,
|
|
3428
|
-
openOrdersCount: (platformOpenOrders === null || platformOpenOrders === void 0 ? void 0 : platformOpenOrders.length) || 0,
|
|
3429
|
-
agentWalletAddress,
|
|
3430
|
-
agentWalletStatus
|
|
3431
|
-
});
|
|
3432
3352
|
// Create calculator and compute account summary
|
|
3433
3353
|
const calculator = new AccountSummaryCalculator(webData2);
|
|
3434
3354
|
const calculated = calculator.calculateAccountSummary(platformAccountSummary, platformOpenOrders, agentWalletAddress, agentWalletStatus);
|
|
3435
|
-
console.log('[useCalculatedAccountSummary] Calculation completed', {
|
|
3436
|
-
hadPlatformSummary: !!platformAccountSummary,
|
|
3437
|
-
calculatedSummary: !!calculated,
|
|
3438
|
-
withdrawable: (_a = calculated === null || calculated === void 0 ? void 0 : calculated.balanceSummary) === null || _a === void 0 ? void 0 : _a.withdrawable,
|
|
3439
|
-
accountValue: (_c = (_b = calculated === null || calculated === void 0 ? void 0 : calculated.balanceSummary) === null || _b === void 0 ? void 0 : _b.crossMarginSummary) === null || _c === void 0 ? void 0 : _c.accountValue
|
|
3440
|
-
});
|
|
3441
3355
|
return calculated;
|
|
3442
3356
|
}, [platformAccountSummary, platformOpenOrders, webData2, agentWalletAddress, agentWalletStatus]);
|
|
3443
3357
|
return calculatedAccountSummary;
|
|
3444
3358
|
};
|
|
3445
3359
|
|
|
3446
3360
|
/**
|
|
3447
|
-
* Hook to access trade histories
|
|
3361
|
+
* Hook to access trade histories with loading state
|
|
3448
3362
|
*/
|
|
3449
3363
|
const useTradeHistories = () => {
|
|
3450
3364
|
const context = useContext(PearHyperliquidContext);
|
|
3451
3365
|
if (!context) {
|
|
3452
3366
|
throw new Error('useTradeHistories must be used within a PearHyperliquidProvider');
|
|
3453
3367
|
}
|
|
3454
|
-
|
|
3368
|
+
const isLoading = useMemo(() => {
|
|
3369
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3370
|
+
return context.data.tradeHistories === null && context.isConnected;
|
|
3371
|
+
}, [context.data.tradeHistories, context.isConnected]);
|
|
3372
|
+
return {
|
|
3373
|
+
data: context.data.tradeHistories,
|
|
3374
|
+
isLoading
|
|
3375
|
+
};
|
|
3455
3376
|
};
|
|
3456
3377
|
/**
|
|
3457
|
-
* Hook to access open positions with real-time calculations
|
|
3378
|
+
* Hook to access open positions with real-time calculations and loading state
|
|
3458
3379
|
*/
|
|
3459
3380
|
const useOpenPositions = () => {
|
|
3460
3381
|
const context = useContext(PearHyperliquidContext);
|
|
@@ -3463,20 +3384,38 @@ const useOpenPositions = () => {
|
|
|
3463
3384
|
}
|
|
3464
3385
|
// Use calculated positions that sync platform data with HyperLiquid real-time data
|
|
3465
3386
|
const calculatedPositions = useCalculatedOpenPositions(context.data.openPositions, context.webData2, context.allMids);
|
|
3466
|
-
|
|
3387
|
+
const isLoading = useMemo(() => {
|
|
3388
|
+
// Loading is true initially and becomes false only when:
|
|
3389
|
+
// 1. WebSocket has open-positions data
|
|
3390
|
+
// 2. webData2 and allMids are ready (required for calculations)
|
|
3391
|
+
// 3. Connection is established
|
|
3392
|
+
return (context.isConnected &&
|
|
3393
|
+
(context.data.openPositions === null || !context.webData2 || !context.allMids));
|
|
3394
|
+
}, [context.data.openPositions, context.webData2, context.allMids, context.isConnected]);
|
|
3395
|
+
return {
|
|
3396
|
+
data: calculatedPositions,
|
|
3397
|
+
isLoading
|
|
3398
|
+
};
|
|
3467
3399
|
};
|
|
3468
3400
|
/**
|
|
3469
|
-
* Hook to access open orders
|
|
3401
|
+
* Hook to access open orders with loading state
|
|
3470
3402
|
*/
|
|
3471
3403
|
const useOpenOrders = () => {
|
|
3472
3404
|
const context = useContext(PearHyperliquidContext);
|
|
3473
3405
|
if (!context) {
|
|
3474
3406
|
throw new Error('useOpenOrders must be used within a PearHyperliquidProvider');
|
|
3475
3407
|
}
|
|
3476
|
-
|
|
3408
|
+
const isLoading = useMemo(() => {
|
|
3409
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3410
|
+
return context.data.openOrders === null && context.isConnected;
|
|
3411
|
+
}, [context.data.openOrders, context.isConnected]);
|
|
3412
|
+
return {
|
|
3413
|
+
data: context.data.openOrders,
|
|
3414
|
+
isLoading
|
|
3415
|
+
};
|
|
3477
3416
|
};
|
|
3478
3417
|
/**
|
|
3479
|
-
* Hook to access account summary with real-time calculations
|
|
3418
|
+
* Hook to access account summary with real-time calculations and loading state
|
|
3480
3419
|
*/
|
|
3481
3420
|
const useAccountSummary = () => {
|
|
3482
3421
|
var _a, _b, _c, _d;
|
|
@@ -3486,8 +3425,15 @@ const useAccountSummary = () => {
|
|
|
3486
3425
|
}
|
|
3487
3426
|
// Use calculated account summary that syncs platform data with HyperLiquid real-time data
|
|
3488
3427
|
const calculatedAccountSummary = useCalculatedAccountSummary(context.data.accountSummary, context.data.openOrders, context.webData2, (_b = (_a = context.data.accountSummary) === null || _a === void 0 ? void 0 : _a.agentWallet) === null || _b === void 0 ? void 0 : _b.address, (_d = (_c = context.data.accountSummary) === null || _c === void 0 ? void 0 : _c.agentWallet) === null || _d === void 0 ? void 0 : _d.status);
|
|
3489
|
-
|
|
3428
|
+
const isLoading = useMemo(() => {
|
|
3429
|
+
// Loading is true initially and becomes false once we get the first data
|
|
3430
|
+
return context.data.accountSummary === null && context.isConnected;
|
|
3431
|
+
}, [context.data.accountSummary, context.isConnected]);
|
|
3432
|
+
return {
|
|
3433
|
+
data: calculatedAccountSummary,
|
|
3434
|
+
isLoading
|
|
3435
|
+
};
|
|
3490
3436
|
};
|
|
3491
3437
|
|
|
3492
|
-
export { AccountSummaryCalculator,
|
|
3438
|
+
export { AccountSummaryCalculator, PearHyperliquidClient, PearHyperliquidProvider, PearMigrationSDK, PearHyperliquidClient as default, useAccountSummary, useAddress, useCalculatedAccountSummary, useCalculatedOpenPositions, useHyperliquidNativeWebSocket, useHyperliquidWebSocket, useMigrationSDK, useOpenOrders, useOpenPositions, usePearHyperliquidClient, useTradeHistories };
|
|
3493
3439
|
//# sourceMappingURL=index.esm.js.map
|