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:
k
2026-05-29 15:15:41 +08:00
parent 3ac1431336
commit a8522a2bf8
3 changed files with 39 additions and 27 deletions
+15 -8
View File
@@ -43,22 +43,27 @@ export default function MacroVibesPage({
const [sentFilter, setSentFilter] = useState<SentimentFilter>('all')
const [tab, setTab] = useState<BtcTab>('bottom')
// Tab-scoped post source. 'bottom' = MVRV/200WMA confluence,
// 'funding' = funding-rate extreme reversal.
const tabSource = tab === 'bottom' ? 'btc_bottom_reversal' : 'funding_reversal'
useEffect(() => {
// BTC on-chain signals update daily — 30 min TTL is safe
// Fetch BY SOURCE, not the latest-500 global feed. These scanner signals
// are rare (24/cycle, hourly) and were being pushed off the latest-500
// page by frequent Trump posts — making the Macro page look empty even
// when signals existed. Server-side source filter guarantees we get them.
setLoading(true)
swrFetch(
'posts-500',
`posts-src-${tabSource}`,
3 * 60_000,
() => getPosts(500, 1),
() => getPosts(200, 1, tabSource),
fresh => setPosts(fresh),
)
.then(p => { setPosts(p); setLoadErr('') })
.catch(e => setLoadErr(e instanceof Error ? e.message : (isZh ? '信号加载失败' : 'Failed to load signals')))
.finally(() => setLoading(false))
}, [isZh])
}, [tabSource, isZh])
// Tab-scoped post source. 'bottom' = MVRV/200WMA confluence,
// 'funding' = funding-rate extreme reversal.
const tabSource = tab === 'bottom' ? 'btc_bottom_reversal' : 'funding_reversal'
const btcPosts = useMemo(
() => posts.filter(p => (p.source || '') === tabSource),
[posts, tabSource],
@@ -270,7 +275,9 @@ function FundingPanel({
BTC perp funding · live
</div>
<div style={{ fontSize: 11, color: 'var(--ink-4)' }}>
{`${snap.cadence_hours}h cadence · ${snap.coverage_days}d coverage · Binance`}
{`${snap.cadence_hours}h cadence · ${snap.coverage_days}d coverage · ${
snap.venue ? snap.venue.charAt(0).toUpperCase() + snap.venue.slice(1) : 'Binance'
}`}
</div>
</div>