- All four signal pipelines in one place — Trump posts, BTC macro
- bottom, funding-rate extremes, and KOL talks-vs-trades — alongside
- your auto-trader's live state.
+ Four signals that move crypto before the crowd sees them — Trump
+ posts, BTC macro bottoms, funding extremes, and what KOLs do vs
+ say. Tracked live, in public, every call timestamped.
diff --git a/app/[locale]/analytics/AnalyticsPageClient.tsx b/app/[locale]/analytics/AnalyticsPageClient.tsx
index 821d5ac..c587cfa 100644
--- a/app/[locale]/analytics/AnalyticsPageClient.tsx
+++ b/app/[locale]/analytics/AnalyticsPageClient.tsx
@@ -1,6 +1,6 @@
'use client'
-import { useState, useEffect, useRef } from 'react'
+import { useState, useEffect, useRef, useMemo } from 'react'
import { useLocale } from 'next-intl'
import { useAccount, useSignMessage } from 'wagmi'
import { getTrades, getSignalAccuracy } from '@/lib/api'
@@ -67,6 +67,7 @@ export default function AnalyticsPageClient() {
const [trades, setTrades] = useState([])
const [accuracy, setAccuracy] = useState(null)
const [period, setPeriod] = useState('30d')
+ const [tradeMode, setTradeMode] = useState<'live' | 'paper'>('live')
const [privateLocked, setPrivateLocked] = useState(false)
const [unlocking, setUnlocking] = useState(false)
const [unlockErr, setUnlockErr] = useState('')
@@ -131,7 +132,21 @@ export default function AnalyticsPageClient() {
}
}
- const filteredTrades = trades.filter((trade) => inPeriod(trade.closed_at, period))
+ // CRITICAL: never mix paper (simulated) and live (real-money) P&L into one
+ // number. is_paper comes from the backend (hl_order_id == "paper"). Default
+ // to LIVE — this page is the "did the bot make REAL money" view. A Paper/Live
+ // toggle appears only when the wallet has both kinds of trades; otherwise we
+ // auto-pick whichever set exists so a paper-only user still sees their data
+ // (clearly labelled SIMULATED).
+ const liveTrades = useMemo(() => trades.filter(t => !t.is_paper), [trades])
+ const paperTrades = useMemo(() => trades.filter(t => !!t.is_paper), [trades])
+ const hasBoth = liveTrades.length > 0 && paperTrades.length > 0
+ const effectiveMode: 'live' | 'paper' =
+ hasBoth ? tradeMode : (liveTrades.length > 0 ? 'live' : (paperTrades.length > 0 ? 'paper' : 'live'))
+ const modeTrades = effectiveMode === 'paper' ? paperTrades : liveTrades
+ const isPaperView = effectiveMode === 'paper'
+
+ const filteredTrades = modeTrades.filter((trade) => inPeriod(trade.closed_at, period))
const pricedTrades = filteredTrades.filter(
(t) => t.pnl_usd !== null && t.pnl_usd !== undefined,
)
@@ -172,13 +187,38 @@ export default function AnalyticsPageClient() {
and AI signal accuracy across the time window you pick on the right.