Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions src/apps/perps/hooks/useHyperliquid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,23 @@ export function useHyperliquid() {
});
isImported = true;
} else {
// 2. Fallback to Connected Wallet
const walletProvider = kit.getEtherspotProvider();
const eoa = (await walletProvider.getSdk()).getEOAAddress() || null;
console.log('DEBUG: Wallet Provider EOA:', eoa);

if (eoa && clientTransport) {
targetAddress = eoa;
client = createWalletClient({
account: eoa as `0x${string}`,
chain: arbitrum,
transport: clientTransport,
});
// 2. Fallback to Connected Wallet
try {
const walletProvider = kit.getEtherspotProvider();
const eoa = (await walletProvider.getSdk()).getEOAAddress() || null;
console.log('DEBUG: Wallet Provider EOA:', eoa);

if (eoa && clientTransport) {
targetAddress = eoa;
client = createWalletClient({
account: eoa as `0x${string}`,
chain: arbitrum,
transport: clientTransport,
});
}
} catch (err) {
console.warn('Failed to get Etherspot provider or EOA (expected in delegatedEoa mode without imported account):', err);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ exports[`<LeftColumnTokenMarketDataRow /> - ETH token row > renders and matches
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -106,7 +106,7 @@ exports[`<LeftColumnTokenMarketDataRow /> - ETH token row > renders and matches
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -260,7 +260,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -421,7 +421,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -584,7 +584,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -774,7 +774,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -937,7 +937,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -1098,7 +1098,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down Expand Up @@ -1261,7 +1261,7 @@ exports[`<TokensWithMarketDataTile /> > renders and matches snapshot 1`] = `
<p
class="text-sm font-medium font-normal text-white"
>
9mo ago
10mo ago
</p>
<p
class="text-sm font-medium font-normal mobile:hidden text-white"
Expand Down
154 changes: 154 additions & 0 deletions src/apps/pillarx-app/hooks/useAlgoInsightsStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { useMemo } from 'react';
import { useTradingSignals } from '../../insights/hooks/useTradingSignals';
import { TradingSignal as InsightSignal } from '../../insights/types';

export interface AlgoInsightsStats {
pnl1m: number;
pnl3m: number;
pnl6m: number;
riskLevel: string;
pnlStatus: {
winning: number;
losing: number;
neutral: number;
};
cumulativePnl: {
'1w': { value: number; history: { timestamp: number; value: number }[] };
'1m': { value: number; history: { timestamp: number; value: number }[] };
'3m': { value: number; history: { timestamp: number; value: number }[] };
'6m': { value: number; history: { timestamp: number; value: number }[] };
};
}

export const useAlgoInsightsStats = () => {
const { signals, loading } = useTradingSignals({ enabled: true });

const stats = useMemo<AlgoInsightsStats | null>(() => {
if (loading || signals.length === 0) return null;
Comment on lines +23 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing error state propagation.

The useTradingSignals hook returns an error state (per the relevant code snippet), but it's not destructured or exposed. If signal fetching fails, consumers cannot distinguish between "no data available" and "fetch error occurred" since both result in stats: null.

Consider exposing the error state:

🛡️ Proposed fix to propagate error state
 export const useAlgoInsightsStats = () => {
-  const { signals, loading } = useTradingSignals({ enabled: true });
+  const { signals, loading, error } = useTradingSignals({ enabled: true });

   const stats = useMemo<AlgoInsightsStats | null>(() => {
     // ...existing code...
   }, [signals, loading]);

-  return { stats, loading };
+  return { stats, loading, error };
 };
🤖 Prompt for AI Agents
In `@src/apps/pillarx-app/hooks/useAlgoInsightsStats.ts` around lines 23 - 27, The
hook useAlgoInsightsStats currently ignores the error returned by
useTradingSignals; destructure the error (e.g., const { signals, loading, error
} = useTradingSignals({ enabled: true })) and propagate it in the hook's return
value so consumers can distinguish fetch failures from empty data (update the
returned shape to include error alongside stats and loading, and ensure stats
remains null when loading or error is present). Ensure references to
useAlgoInsightsStats and useTradingSignals are updated so callers can handle
error state.


// Helper to calculate PnL for a set of signals
const calculatePnL = (filterFn: (s: InsightSignal) => boolean) => {
return signals
.filter(filterFn)
.reduce((sum, s) => sum + (s.realized_pnl_percent || 0), 0);
};

const now = Date.now();
const oneMonthAgo = now - 30 * 24 * 60 * 60 * 1000;
const threeMonthsAgo = now - 90 * 24 * 60 * 60 * 1000;
const sixMonthsAgo = now - 180 * 24 * 60 * 60 * 1000;

// PnL metrics
const pnl1m = calculatePnL((s) => {
const closedAt = s.closed_at ? new Date(s.closed_at).getTime() : 0;
return closedAt > oneMonthAgo;
});

const pnl3m = calculatePnL((s) => {
const closedAt = s.closed_at ? new Date(s.closed_at).getTime() : 0;
return closedAt > threeMonthsAgo;
});

const pnl6m = calculatePnL((s) => {
const closedAt = s.closed_at ? new Date(s.closed_at).getTime() : 0;
return closedAt > sixMonthsAgo;
});

// PnL Status (Winning/Losing/Neutral)
const closedSignals = signals.filter(
(s) =>
s.status === 'closed' ||
s.status === 'completed' ||
s.status === 'stopped'
);
const totalClosed = closedSignals.length;

let winning = 0;
let losing = 0;
let neutral = 0;

if (totalClosed > 0) {
winning =
(closedSignals.filter((s) => (s.realized_pnl_percent || 0) > 0).length /
totalClosed) *
100;
losing =
(closedSignals.filter((s) => (s.realized_pnl_percent || 0) < 0).length /
totalClosed) *
100;
neutral =
(closedSignals.filter((s) => (s.realized_pnl_percent || 0) === 0)
.length /
totalClosed) *
100;
}

// Cumulative PnL History Generation
const generateHistory = (cutoffTime: number) => {
// Sort signals by close time
const relevantSignals = signals
.filter(
(s) => s.closed_at && new Date(s.closed_at).getTime() > cutoffTime
)
.sort(
(a, b) =>
new Date(a.closed_at!).getTime() - new Date(b.closed_at!).getTime()
);

let runningPnL = 0;
const history = relevantSignals.map((s) => {
runningPnL += s.realized_pnl_percent || 0;
return {
timestamp: new Date(s.closed_at!).getTime() / 1000,
value: runningPnL,
};
});

// Ensure we have a starting point? Or just the trade history.
// If history is empty, return empty array
return history;
};

// For cumulative values in the object, we use the specific period totals calculated above
// but the history needs to be generated point-by-point.

return {
pnl1m: Number(pnl1m.toFixed(2)),
pnl3m: Number(pnl3m.toFixed(2)),
pnl6m: Number(pnl6m.toFixed(2)),
riskLevel: 'Low Risk', // Placeholder logic
pnlStatus: {
winning: Number(winning.toFixed(1)),
losing: Number(losing.toFixed(1)),
neutral: Number(neutral.toFixed(1)),
},
cumulativePnl: {
'1w': {
value: Number(
calculatePnL((s) =>
s.closed_at
? new Date(s.closed_at).getTime() >
now - 7 * 24 * 60 * 60 * 1000
: false
).toFixed(2)
),
history: generateHistory(now - 7 * 24 * 60 * 60 * 1000),
},
'1m': {
value: Number(pnl1m.toFixed(2)),
history: generateHistory(oneMonthAgo),
},
'3m': {
value: Number(pnl3m.toFixed(2)),
history: generateHistory(threeMonthsAgo),
},
'6m': {
value: Number(pnl6m.toFixed(2)),
history: generateHistory(sixMonthsAgo),
},
},
};
}, [signals, loading]);

return { stats, loading };
};