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