Lets the user collapse every post the AI judged off-topic (not crypto-related,
never scored) so the feed shows only real signals.
- PostCards exports isAiScored(post) as the single source of truth for what
counts as noise — the card de-emphasis and the list filter now share it, so
they can never disagree.
- TrumpPageClient: 'Hide off-topic (N)' toggle in the filter row (only shown
when N>0), with live noise count. Resets to page 1 on toggle.
tsc + build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Follow-up to fe88acb. Noise posts (not crypto-relevant, never AI-scored) now
read as 'skip me' at a glance in the list — no need to open them:
- sentiment chip → muted 'off-topic · not crypto' tag (was a colored chip)
- post text → dimmer (ink-4) + shorter preview (90 vs 180 chars)
- aside → single muted 'SKIPPED' tag instead of HOLD pill + empty AI metric
Scored posts (real crypto signals) are visually unchanged — full signal pill,
target chip, impact, and AI confidence.
tsc + build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Most Trump posts aren't crypto-related, so the entry filter / AI marks them
relevant=false with ai_confidence=0 and no reasoning (never AI-scored). The
post card still rendered 'AI confidence 0%' with an empty progress bar, which
reads as 'AI looked and was 0% confident' rather than the truth: 'skipped as
not crypto-relevant'.
Added an aiScored guard (ai_confidence > 0 || ai_reasoning present):
- collapsed row: shows AI '—' instead of '0%' for unscored posts
- expanded detail: shows 'Not crypto-relevant — skipped AI scoring' instead
of an empty 0% confidence bar
Scored posts (the ~80 with confidence>0 in test data, and all real signals
once live) are unchanged — full bar + reasoning shown.
tsc + build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GEO (AI answer engines) — make the signal feeds and case studies machine-citeable:
- Dataset JSON-LD on /trump, /kol, /trades (creator=Endorphin, publisher→#org,
isAccessibleForFree, temporalCoverage). These are the "data/history/track
record" entities Perplexity/Gemini/ChatGPT cite.
- case-studies: ItemList of 5 Article nodes, each with headline/description/
articleBody/about + citation→evidence URL + author=Endorphin. ISO dates
derived where parseable, omitted for vague labels.
- public/llms-full.txt: single-doc full reference (methodology + glossary +
case studies w/ sources inlined) for one-fetch AI ingestion; linked from llms.txt.
SEO:
- Breadcrumbs component (components/seo/Breadcrumbs.tsx) → BreadcrumbList JSON-LD
on the 8 indexable content pages (trump/kol/macro/trades/analytics/
methodology/glossary/case-studies).
- /archive split into server page + ArchivePageClient; server page sets
robots noindex (legacy/test data — thin-content risk).
- openGraph added to /trades and /analytics (previously meta+canonical only).
Verified: tsc 0 errors, next build passes, runtime curl confirms all JSON-LD
types render and llms-full.txt serves 200. No trading/API/component-logic changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Money-safety/accuracy bug: analytics computed total P&L, win rate, drawdown,
and every metric over ALL trades regardless of is_paper. A user who tried
paper mode then went live saw simulated and real P&L summed into one number
on the page whose entire purpose is 'did the bot make REAL money'.
Now: split trades by is_paper. Default to LIVE. A Live/Paper toggle appears
only when the wallet has both; otherwise auto-pick whichever exists. Paper
view shows a prominent 'SIMULATED — not real-money results' banner. All
metrics derive from the selected mode's trades only.
Also: BotConfigPanel handleUpgradeToLive copy now states Auto-Trade is forced
OFF on the paper→live switch (matches backend subscribe.py safety reset).
tsc + next build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Canonical URL fixes (parity with trump/macro/kol done earlier):
- analytics/page.tsx: canonical was `${siteUrl}/${locale}/analytics` → /en/
- settings/page.tsx: canonical was `${siteUrl}/${locale}/settings` → /en/
Settings scope card anchor navigation:
- Three scope cards ('Trump Signal ↓', 'Macro Vibes ↓', 'Global ↓') all
pointed to #bot-config — same destination, no differentiation. Renamed:
#config-trump / #config-macro / #config-global.
- Added matching id='config-trump/macro/global' to the three sections in
BotConfigPanel so the anchors actually land at the right section.
- Removed misleading 'tab' wording ('Trump Signal tab ↓') — BotConfigPanel
is not a tab component; it's a single-page vertical form. Copy now reads
'Configure Trump ↓' etc.
- SystemControl already links to settings#bot-config which still resolves
(the outer #bot-config wrapper div in SettingsClient is unchanged).
tsc + next build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SystemControl was rendered as two per-system panels (Trump / Macro) that both
flipped the SAME global auto_trade, and its copy claimed a "qualifying Macro
Vibes signal auto-opens a trade". Both are wrong: bot_engine.process_post
early-returns for System 2, so the auto_trade gate only ever fires for Trump
(System 1). Macro Vibes is manage-only — it never auto-opens.
- Macro (`system="btc"`) panel: replaced the misleading Auto-Trade ON/OFF
toggle with a manage-only explainer (open on HL → /adopt via Telegram → bot
manages exits) and a note that the Trump-page switch has no effect here.
- Trump (`system="trump"`) panel: relabeled the switch "TRUMP (System 1)" and
rewrote all copy/status lines to say Trump-only auto-open (was "GLOBAL
(Trump + BTC)" / "both Trump & BTC").
- CLAUDE.md: corrected "No global store (no Redux/Zustand)" — the app DOES use
a small Zustand store (store/dashboard.ts / useDashboardStore); documented
what it holds and the guidance to keep it small.
Note (not a bug): dashboard adopt/release is intentionally Telegram-only —
there are no positions/adopt|hl|release client calls and the UI correctly
points to the Telegram /adopt command. No dashboard wiring is "broken".
tsc + next build clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
BotConfigPanel (full rewrite, 1113→858 lines):
- Remove 3-tab layout; replace with two expandable cards (Trump Signal /
Macro Vibes) + collapsed Advanced block — fewer decisions, less noise
- Fix paper mode bug: UserResponse now returns paper_mode; BotConfigPanel
tracks it in state; missingItems skips HL key requirement for paper users;
HL key card shows paper status instead of empty input
- Add "Switch to live →" upgrade path — calls /subscribe with paper_mode=false
so paper users can promote without re-subscribing from scratch
- Add confirmSign before manual override window (consistent with all other
mutations); rename "Arm/Disarm" → "Override/Cancel override" throughout
- Fix all ambiguous copy: subscribe card, Macro Vibes description (manual
/adopt flow, not auto-manage), missingItems strings, TP/SL "Not saved yet"
→ "Required — not saved", "Daily spend cap" → "Daily trading cap",
"Active schedule" → "Trading schedule", "Get started" confirmSign labels
lib/api.ts:
- Add paper_mode: boolean to UserData interface (was missing)
OpenPositions:
- "Load your settings once…" → "Sign in once to view open positions" (removes
implementation-detail explanation)
- "Today realised" → "Today's realized"; "banked" → "locked in" throughout
- "The bot will appear here" → "When the bot opens a trade, it will show up here"
SystemControl:
- "Net result" → "Current status"
settings/SettingsClient.tsx:
- id="btc-settings" → id="macro-settings" (stale rename)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
User report: the dashed vertical line + "MACRO BOTTOM" label could be
visually "dragged" — it wasn't actually draggable, but it stayed glued
to a fixed pixel column on screen while the candles slid out from under
it on pan, so the label ended up pointing at unrelated candles.
Root cause: the line + label are absolutely-positioned DOM nodes (the
lightweight-charts library has no first-class vertical-marker primitive).
The data effect set their `left` ONCE via timeScale.timeToCoordinate()
and then never updated again. Every user interaction that changes the
visible range — pan, wheel zoom, programmatic setVisibleRange, window
resize — invalidates that pixel coordinate.
Fix:
- Store the signal's bucketTime + label text in refs.
- Hoist the "project bucketTime → pixel X" logic into a single
`reposition()` fn captured in repositionMacroRef.
- Subscribe to `timeScale().subscribeVisibleTimeRangeChange(reposition)`
so it fires on every pan / zoom / data update.
- Wire it into the ResizeObserver too (width change ⇒ new pixel space).
- Re-run from the data effect whenever the signal changes.
- Hide (opacity: 0) instead of clamping when the signal candle scrolls
outside the visible range — a clamped label glued to the chart edge
is actively misleading.
- Drop `transition: left 160ms` on both nodes — that transition fires
on every pan frame and makes the marker LAG behind the candles,
reproducing the original visual bug for fast pans.
Verified via DOM scripting:
pan right ⇒ label_left 744px → 344px (follows candles ✓)
big right-pan past signal ⇒ opacity 1 → 0 (hides cleanly ✓)
zoom out ⇒ opacity 0 → 1, label re-projects to new pixel space ✓
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
for Performance accent
User flagged the macro index box on the overview page as feeling
disjointed. Reshaped it from three stacked elements (band → track →
scale) into a single bordered card so it reads as ONE component.
- JSX: wrap the three pieces in <div className="overview-macro-card">
- CSS: new .overview-macro-card with tone-coloured rim (bull/bear/neutral)
- Solid neutral-gray filled needle (was a hollow ring — looked like a
placeholder); tone-coloured background + white inner ring + double
shadow so it stands out on any gradient position
- Removed the .overview-score-fill overlay — the gradient already
encodes the spectrum; layering an opaque fill obscured it near 0
- Thinner track (14px vs 22px), tighter scale labels, smaller pill
- Added "TODAY · 8 INDICATORS" stamp next to the title — gives users
a quick anchor of what they're looking at + freshness
Plus: dark-mode override for .overview-stat-card.accent (the Performance
card). It was using a cream gradient that floated as a glaring
out-of-theme block on dark mode. Mirrored the existing .kpi.accent dark
treatment so it stays visually grouped with the rest of the dashboard.
Also includes the in-flight overview rewrite from the other AI tool
(legacy /btc redirect to /macro, middleware.ts replacing proxy.ts for
Next.js routing, refactored several dashboard panels). TypeScript clean,
production build passes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Big-picture changes since 01be8e7:
New routes — /trump /btc /kol /trades /analytics /archive plus four
SEO landing pages (/methodology /glossary /case-studies /contact),
all served under /[locale]. Each has dedicated metadata + JSON-LD.
KOL page (new) — DigestWidget + OnchainWidget + TalkVsTradesWidget,
filter by handle/ticker, click-through to per-post detail with the
original AI-extracted ticker/conviction/quote.
BTC page (new) — tabbed: macro-bottom (AHR999 + 200WMA + Pi Cycle)
and funding-rate reversal, with live sparkline + threshold bands.
Telegram card on Settings — wallet-link code generation, status,
disconnect. Preferences moved into the bot itself (/trump /btc etc.)
so the card stays minimal.
SignalMonitor (new) — ETH/LINK Bollinger breakout monitor in its own
component, shares the singleton WsProvider so no second WS opens.
WS singleton refactor (lib/wsContext) — shared WsProvider + useWsSubscribe
hook. Cleanup now actively closes the socket on unmount; previously the
local `ws` couldn't be reached from cleanup and leaked one connection
per StrictMode remount.
OpenPositions polling no longer pops MetaMask in the background —
splits into load('first') for user-initiated and load('poll') that
uses getCachedViewEnvelope without signing.
i18n shelved — proxy.ts (Next 16 middleware rename) wires next-intl
but only Navbar + layout footer have translations. Rest of UI used
isZh ternary scattered across 28 files. All `const isZh = locale === 'zh'`
flipped to `const isZh = false` so every site renders English; Chinese
branches kept as dead code so revival is one regex away.
LanguageSwitch hidden but file kept. zh-CN hreflang removed from
metadata + sitemap to avoid duplicate-content penalties.
Wallet error handling — lib/walletError.ts: isUserRejection walks EIP-1193
code 4001 + .cause chain; previous string-match for "reject"/"denied"
broke for users running MetaMask in non-English UIs. 12 call sites
migrated across 5 components.
PWA icons — app/icon.tsx + app/apple-icon.tsx render the brand "α" via
Next's ImageResponse so no static PNG asset is required. manifest.ts
references /icon and /apple-icon dynamic routes.
OG image + sitemap + robots — dynamic 1200×630 OG card; robots blocks
both /en/settings and /zh/settings; sitemap only emits /en routes;
JSON-LD covers SoftwareApplication + Organization + 10-Q FAQPage.
Landing page polish — Launch Dashboard button stripped of magnetic
hover + shimmer + lift + glow expansion (multiple users found it busy);
hero scramble alphabet swapped to alphanumeric (was block characters);
"15 KOL feeds" copy updated to 19 in 5 places.
PostCards source icons — new entries for btc_bottom_reversal,
funding_reversal, kol_divergence so they no longer fall through to
the generic "first letter" fallback.
Archive page filter — excludes funding_reversal + kol_divergence
(previously only excluded truth + btc_bottom_reversal so new live
signals leaked into the legacy archive).
Cache + skeleton loading — lib/cache.ts SWR module with per-key TTL,
applied across the major pages so navigation feels instant.
SignConfirmSheet — 4 hardcoded Chinese strings translated to English
(English users were seeing zh-only "需要钱包签名" etc).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Five bugs in the same blast radius (places we render a price/percent).
DashboardClient.tsx
• Hero price read lastCandle.close (REST candle, frozen until user
changes asset/tf). WebSocket livePrices was set but never read.
Now: livePrice ?? lastCandle.close.
• "+21.84% · 4H" was (last_close - first_open) / first_open over
the entire candle window. limit=200 + tf=4H = 33 days shown as
"4H". Find candle closest to now-24h, label "24h" consistently.
ChartPanel.tsx
• Chart only re-rendered on candles prop change. Live WS ticks
ignored. Added useEffect calling series.update() with
livePrices[asset] on every tick.
analytics/page.tsx
• reduce(sum + trade.pnl_usd) when pnl_usd is null → NaN poison.
• Math.max/min over null cast to 0 → bogus "best/worst trade".
• Win-rate denominator counted null-pnl trades as losses.
• calcDrawdownPct had the same null poisoning.
Fix: gate aggregations on priced subset only.
trades/page.tsx
• ROI could divide by 0/null; entry/exit crashed on null.toLocaleString().
Added guards.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Major UI updates across dashboard, analytics, posts, trades, settings
- New landing page, robots/sitemap, contact/privacy/terms pages
- Updated globals.css with extensive styling and new landing.css
- Refactor signedRequest, realtime data hook, and dashboard store
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>