fix: Macro fetches by source, Analytics single-basis stats, funding venue label
- Macro page fetches getPosts(200, 1, source) per active tab instead of filtering the latest-500 global feed — rare bottom/funding signals no longer vanish behind Trump posts (backend now supports ?source=). - Analytics: compute ALL time windows from the one closed_at-filtered trades list; dropped the 30d-only backend /performance special-case that mixed an opened_at basis into the same screen as the closed_at metric grid. Trade fetch raised 100→500 so the local computation covers ample history. - Funding panel shows snap.venue (capitalized) instead of hardcoded "Binance". - lib/api: getPosts gains optional source param; FundingSnapshot gains venue. tsc + next build clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useLocale } from 'next-intl'
|
||||
import { useAccount, useSignMessage } from 'wagmi'
|
||||
import { getPerformance, getTrades, getSignalAccuracy } from '@/lib/api'
|
||||
import type { BotPerformance, BotTrade } from '@/types'
|
||||
import { getTrades, getSignalAccuracy } from '@/lib/api'
|
||||
import type { BotTrade } from '@/types'
|
||||
import type { SignalAccuracy } from '@/lib/api'
|
||||
import { getCachedViewEnvelope, getOrCreateViewEnvelope, type SignedEnvelope } from '@/lib/signedRequest'
|
||||
import PageHint from '@/components/ui/PageHint'
|
||||
@@ -64,7 +64,6 @@ export default function AnalyticsPageClient() {
|
||||
const isZh = false // i18n shelved — Chinese branches kept as dead code for future revival; see messages/zh.json
|
||||
const { address, isConnected } = useAccount()
|
||||
const { signMessageAsync } = useSignMessage()
|
||||
const [perf, setPerf] = useState<BotPerformance | null>(null)
|
||||
const [trades, setTrades] = useState<BotTrade[]>([])
|
||||
const [accuracy, setAccuracy] = useState<SignalAccuracy | null>(null)
|
||||
const [period, setPeriod] = useState<Period>('30d')
|
||||
@@ -81,24 +80,29 @@ export default function AnalyticsPageClient() {
|
||||
// `forcedEnv` (a freshly-minted view_user) lets the in-page Unlock button
|
||||
// load private data without a detour through the Settings page. Public
|
||||
// signal-accuracy always loads regardless.
|
||||
//
|
||||
// All displayed stats are computed locally from this single trades list on
|
||||
// ONE basis (closed_at, see inPeriod) for EVERY time window — we no longer
|
||||
// mix in the backend /performance aggregate for 30d, which used a different
|
||||
// basis (it filtered opened_at and capped at 30d) and produced numbers that
|
||||
// disagreed with the closed_at-based metric grid on the same screen. Limit
|
||||
// raised to 500 so the local computation covers ample history.
|
||||
async function loadAll(forcedEnv?: SignedEnvelope) {
|
||||
if (!address || !isConnected) {
|
||||
setPerf(null); setTrades([]); setAccuracy(null); setPrivateLocked(false)
|
||||
setTrades([]); setAccuracy(null); setPrivateLocked(false)
|
||||
return
|
||||
}
|
||||
const sharedEnv = forcedEnv ?? getCachedViewEnvelope('view_user', address)
|
||||
const perfEnv = getCachedViewEnvelope('view_performance', address) ?? sharedEnv
|
||||
const tradesEnv = getCachedViewEnvelope('view_trades', address) ?? sharedEnv
|
||||
if (aliveRef.current) setPrivateLocked(!perfEnv && !tradesEnv)
|
||||
const tradesEnv = getCachedViewEnvelope('view_trades', address)
|
||||
?? (forcedEnv ?? getCachedViewEnvelope('view_user', address))
|
||||
if (aliveRef.current) setPrivateLocked(!tradesEnv)
|
||||
try {
|
||||
const [p, t, a] = await Promise.all([
|
||||
perfEnv ? getPerformance(address, perfEnv).catch(() => null) : Promise.resolve(null),
|
||||
tradesEnv ? getTrades(address, tradesEnv, 100, 1).catch(() => []) : Promise.resolve([]),
|
||||
const [t, a] = await Promise.all([
|
||||
tradesEnv ? getTrades(address, tradesEnv, 500, 1).catch(() => []) : Promise.resolve([]),
|
||||
getSignalAccuracy().catch(() => null),
|
||||
])
|
||||
if (aliveRef.current) { setPerf(p); setTrades(t); setAccuracy(a) }
|
||||
if (aliveRef.current) { setTrades(t); setAccuracy(a) }
|
||||
} catch {
|
||||
if (aliveRef.current) { setPerf(null); setTrades([]) }
|
||||
if (aliveRef.current) setTrades([])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,10 +143,9 @@ export default function AnalyticsPageClient() {
|
||||
const worstTrade = pnls.length ? Math.min(...pnls) : 0
|
||||
const avgTrade = pnls.length ? totalPnl / pnls.length : 0
|
||||
const avgHold = filteredTrades.length ? filteredTrades.reduce((sum, trade) => sum + trade.hold_seconds, 0) / filteredTrades.length : 0
|
||||
const maxDrawdown = period === '30d' && perf ? perf.max_drawdown_pct : calcDrawdownPct(filteredTrades)
|
||||
const summaryPnl = period === '30d' && perf && trades.length >= filteredTrades.length
|
||||
? perf.net_pnl_usd
|
||||
: totalPnl
|
||||
// One basis for every window: derive from the closed_at-filtered trades.
|
||||
const maxDrawdown = calcDrawdownPct(filteredTrades)
|
||||
const summaryPnl = totalPnl
|
||||
|
||||
const metrics = [
|
||||
{ k: isZh ? '最大回撤' : 'Max drawdown', v: filteredTrades.length ? maxDrawdown.toFixed(1) + '%' : '—', sub: isZh ? '从高点到低点的最大跌幅' : 'Worst peak-to-trough', down: true,
|
||||
|
||||
Reference in New Issue
Block a user