feat(macro-vibes): rename BTC Signal → Macro Vibes; add MacroPanel UI
Module rename across page H1, navbar tab, URL (/en/btc → /en/macro), all metadata/JSON-LD, sitemap, llms.txt, opengraph image, and SystemControl copy. Old /btc route fully removed. New components/btc/MacroPanel.tsx polls /api/macro/snapshot and lays out the 8 indicators in four sections (Valuation / Bottom trigger reference / Market structure / Sentiment & flows / Positioning) with tone-coloured values, current-band threshold chips, and a single CoinGlass / source chart link per card. Composite -100..+100 needle pulses on score change. Also fixes the pinned bottom-reversal alert on the homepage, which still linked to the now-404 /en/btc — now routes to /en/macro. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,8 @@ import { getPerformance, getTrades, getSignalAccuracy } from '@/lib/api'
|
||||
import type { BotPerformance, BotTrade } from '@/types'
|
||||
import type { SignalAccuracy } from '@/lib/api'
|
||||
import { getCachedViewEnvelope, getOrCreateViewEnvelope } from '@/lib/signedRequest'
|
||||
import PageHint from '@/components/ui/PageHint'
|
||||
import InfoTip from '@/components/ui/InfoTip'
|
||||
|
||||
type Period = '7d' | '30d' | '90d' | 'All'
|
||||
|
||||
@@ -115,12 +117,18 @@ export default function AnalyticsPageClient() {
|
||||
: totalPnl
|
||||
|
||||
const metrics = [
|
||||
{ k: isZh ? '最大回撤' : 'Max drawdown', v: filteredTrades.length ? maxDrawdown.toFixed(1) + '%' : '—', sub: isZh ? '从高点到低点的最大跌幅' : 'Worst peak-to-trough', down: true },
|
||||
{ k: isZh ? '总交易数' : 'Total trades', v: String(filteredTrades.length || '—'), sub: isZh ? '已平仓机器人交易' : 'Closed bot executions' },
|
||||
{ k: isZh ? '平均持仓时间' : 'Avg hold time', v: filteredTrades.length ? fmtHold(Math.round(avgHold)) : '—', sub: isZh ? '按单笔计算' : 'Per trade' },
|
||||
{ k: isZh ? '平均单笔盈亏' : 'Avg trade P&L', v: fmtMoney(avgTrade), sub: isZh ? '每笔交易均值' : 'Mean per trade', up: avgTrade > 0 },
|
||||
{ k: isZh ? '最佳单笔' : 'Best trade', v: fmtMoney(bestTrade), sub: isZh ? '单笔最大盈利' : 'Largest single win', up: true },
|
||||
{ k: isZh ? '最差单笔' : 'Worst trade', v: fmtMoney(worstTrade), sub: isZh ? '单笔最大亏损' : 'Largest single loss', down: true },
|
||||
{ k: isZh ? '最大回撤' : 'Max drawdown', v: filteredTrades.length ? maxDrawdown.toFixed(1) + '%' : '—', sub: isZh ? '从高点到低点的最大跌幅' : 'Worst peak-to-trough', down: true,
|
||||
tip: 'Largest equity drop from a previous high. Tells you the worst losing streak you would have lived through.' },
|
||||
{ k: isZh ? '总交易数' : 'Total trades', v: String(filteredTrades.length || '—'), sub: isZh ? '已平仓机器人交易' : 'Closed bot executions',
|
||||
tip: 'How many closed positions in this window. Open trades not counted.' },
|
||||
{ k: isZh ? '平均持仓时间' : 'Avg hold time', v: filteredTrades.length ? fmtHold(Math.round(avgHold)) : '—', sub: isZh ? '按单笔计算' : 'Per trade',
|
||||
tip: 'Entry → exit duration averaged across every closed trade.' },
|
||||
{ k: isZh ? '平均单笔盈亏' : 'Avg trade P&L', v: fmtMoney(avgTrade), sub: isZh ? '每笔交易均值' : 'Mean per trade', up: avgTrade > 0,
|
||||
tip: 'Total P&L ÷ number of trades. Positive = the strategy has edge per trade.' },
|
||||
{ k: isZh ? '最佳单笔' : 'Best trade', v: fmtMoney(bestTrade), sub: isZh ? '单笔最大盈利' : 'Largest single win', up: true,
|
||||
tip: 'Biggest realized gain on one trade in this window.' },
|
||||
{ k: isZh ? '最差单笔' : 'Worst trade', v: fmtMoney(worstTrade), sub: isZh ? '单笔最大亏损' : 'Largest single loss', down: true,
|
||||
tip: 'Biggest realized loss on one trade. Should be bounded by your stop-loss setting.' },
|
||||
]
|
||||
|
||||
return (
|
||||
@@ -128,7 +136,10 @@ export default function AnalyticsPageClient() {
|
||||
<div className="page-head">
|
||||
<div>
|
||||
<h1 className="page-title">{isZh ? '分析面板' : 'Analytics'}</h1>
|
||||
<p className="page-sub">{isZh ? `查看 ${period} 窗口内的信号与交易表现。` : `Deep dive into ${period} of signals and trades.`}</p>
|
||||
<PageHint>
|
||||
Did the bot actually make money? Win rate, drawdown, average trade,
|
||||
and AI signal accuracy across the time window you pick on the right.
|
||||
</PageHint>
|
||||
</div>
|
||||
<div className="nav-tabs">
|
||||
{(['7d', '30d', '90d', 'All'] as const).map(p => (
|
||||
@@ -154,8 +165,11 @@ export default function AnalyticsPageClient() {
|
||||
|
||||
<div className="metric-grid" style={{ marginBottom: 20 }}>
|
||||
{metrics.map(m => (
|
||||
<div key={m.k} className="card" style={{ padding: 20 }}>
|
||||
<div className="tiny" style={{ marginBottom: 8 }}>{m.k}</div>
|
||||
<div key={m.k} className="card" style={{ padding: 20, overflow: 'visible' }}>
|
||||
<div className="tiny" style={{ marginBottom: 8, display: 'flex', alignItems: 'center' }}>
|
||||
{m.k}
|
||||
{m.tip && <InfoTip text={m.tip} placement="top" width={240} />}
|
||||
</div>
|
||||
<div className={`mono ${m.up ? 'delta up' : m.down ? 'delta down' : ''}`} style={{ fontSize: 24, fontWeight: 500, letterSpacing: '-0.01em' }}>{m.v}</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4 }}>{m.sub}</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user