fix(analytics): never mix paper + live P&L; settings live-switch copy
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>
This commit is contained in:
@@ -303,9 +303,9 @@ export default function DashboardClient({ initialPosts }: Props) {
|
|||||||
<div>
|
<div>
|
||||||
<h1 className="page-title">{isZh ? '信号总览' : 'Signal monitor'}</h1>
|
<h1 className="page-title">{isZh ? '信号总览' : 'Signal monitor'}</h1>
|
||||||
<PageHint count={`${actionablePosts} actionable · ${totalPosts} tracked`}>
|
<PageHint count={`${actionablePosts} actionable · ${totalPosts} tracked`}>
|
||||||
All four signal pipelines in one place — Trump posts, BTC macro
|
Four signals that move crypto before the crowd sees them — Trump
|
||||||
bottom, funding-rate extremes, and KOL talks-vs-trades — alongside
|
posts, BTC macro bottoms, funding extremes, and what KOLs do vs
|
||||||
your auto-trader's live state.
|
say. Tracked live, in public, every call timestamped.
|
||||||
</PageHint>
|
</PageHint>
|
||||||
</div>
|
</div>
|
||||||
<div className="row gap-s">
|
<div className="row gap-s">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef, useMemo } from 'react'
|
||||||
import { useLocale } from 'next-intl'
|
import { useLocale } from 'next-intl'
|
||||||
import { useAccount, useSignMessage } from 'wagmi'
|
import { useAccount, useSignMessage } from 'wagmi'
|
||||||
import { getTrades, getSignalAccuracy } from '@/lib/api'
|
import { getTrades, getSignalAccuracy } from '@/lib/api'
|
||||||
@@ -67,6 +67,7 @@ export default function AnalyticsPageClient() {
|
|||||||
const [trades, setTrades] = useState<BotTrade[]>([])
|
const [trades, setTrades] = useState<BotTrade[]>([])
|
||||||
const [accuracy, setAccuracy] = useState<SignalAccuracy | null>(null)
|
const [accuracy, setAccuracy] = useState<SignalAccuracy | null>(null)
|
||||||
const [period, setPeriod] = useState<Period>('30d')
|
const [period, setPeriod] = useState<Period>('30d')
|
||||||
|
const [tradeMode, setTradeMode] = useState<'live' | 'paper'>('live')
|
||||||
const [privateLocked, setPrivateLocked] = useState(false)
|
const [privateLocked, setPrivateLocked] = useState(false)
|
||||||
const [unlocking, setUnlocking] = useState(false)
|
const [unlocking, setUnlocking] = useState(false)
|
||||||
const [unlockErr, setUnlockErr] = useState('')
|
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(
|
const pricedTrades = filteredTrades.filter(
|
||||||
(t) => t.pnl_usd !== null && t.pnl_usd !== undefined,
|
(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.
|
and AI signal accuracy across the time window you pick on the right.
|
||||||
</PageHint>
|
</PageHint>
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-tabs">
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'flex-end' }}>
|
||||||
{(['7d', '30d', '90d', 'All'] as const).map(p => (
|
{hasBoth && (
|
||||||
<button key={p} className={`nav-tab ${period === p ? 'active' : ''}`} onClick={() => setPeriod(p)}>{p}</button>
|
<div className="nav-tabs">
|
||||||
))}
|
{(['live', 'paper'] as const).map(m => (
|
||||||
|
<button key={m}
|
||||||
|
className={`nav-tab ${tradeMode === m ? 'active' : ''}`}
|
||||||
|
onClick={() => setTradeMode(m)}>
|
||||||
|
{m === 'live' ? '💰 Live' : '📝 Paper'}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="nav-tabs">
|
||||||
|
{(['7d', '30d', '90d', 'All'] as const).map(p => (
|
||||||
|
<button key={p} className={`nav-tab ${period === p ? 'active' : ''}`} onClick={() => setPeriod(p)}>{p}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isPaperView && filteredTrades.length > 0 && (
|
||||||
|
<div className="card" style={{
|
||||||
|
padding: '10px 16px', marginBottom: 16, fontSize: 12, fontWeight: 600,
|
||||||
|
background: 'var(--amber-soft)',
|
||||||
|
borderColor: 'color-mix(in oklab, var(--amber) 30%, var(--line))',
|
||||||
|
color: 'var(--amber-ink, var(--ink))',
|
||||||
|
}}>
|
||||||
|
📝 Showing SIMULATED (paper) performance — these are not real-money results.
|
||||||
|
{hasBoth && ' Switch to 💰 Live above for actual P&L.'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{privateLocked && (
|
{privateLocked && (
|
||||||
<div className="card" style={{ padding: 16, marginBottom: 20 }}>
|
<div className="card" style={{ padding: 16, marginBottom: 20 }}>
|
||||||
<div style={{ fontSize: 13, fontWeight: 600 }}>Private performance is locked.</div>
|
<div style={{ fontSize: 13, fontWeight: 600 }}>Private performance is locked.</div>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default async function LocaleLayout({ children, params }: LayoutProps) {
|
|||||||
color: 'var(--ink-3)',
|
color: 'var(--ink-3)',
|
||||||
marginTop: 48,
|
marginTop: 48,
|
||||||
}}>
|
}}>
|
||||||
<span>© {new Date().getFullYear()} Trump Alpha</span>
|
<span>© {new Date().getFullYear()} Trump Alpha — a research project by Endorphin</span>
|
||||||
<Link href={href('/methodology')} style={{ color: 'var(--ink-3)', textDecoration: 'none' }}>{t('methodology')}</Link>
|
<Link href={href('/methodology')} style={{ color: 'var(--ink-3)', textDecoration: 'none' }}>{t('methodology')}</Link>
|
||||||
<Link href={href('/glossary')} style={{ color: 'var(--ink-3)', textDecoration: 'none' }}>{t('glossary')}</Link>
|
<Link href={href('/glossary')} style={{ color: 'var(--ink-3)', textDecoration: 'none' }}>{t('glossary')}</Link>
|
||||||
<Link href={href('/case-studies')} style={{ color: 'var(--ink-3)', textDecoration: 'none' }}>{t('caseStudies')}</Link>
|
<Link href={href('/case-studies')} style={{ color: 'var(--ink-3)', textDecoration: 'none' }}>{t('caseStudies')}</Link>
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ SHORT_INTENT + SHORT_ACTION => ALIGNMENT`,
|
|||||||
heroTitle: 'Signal Methodology',
|
heroTitle: 'Signal Methodology',
|
||||||
heroSubtitle: 'How each engine works, from raw input to trading decision',
|
heroSubtitle: 'How each engine works, from raw input to trading decision',
|
||||||
intro:
|
intro:
|
||||||
'Trump Alpha is not one monolithic strategy. It is a stack of six independent engines, each with its own data source, trigger logic, and failure mode. This page documents the live production logic rather than a simplified marketing summary.',
|
'Endorphin runs Trump Alpha as a research project, not a marketing funnel. It is not one monolithic strategy — it is a stack of six independent engines, each with its own data source, trigger logic, and failure mode. This page documents the live production logic, not a simplified pitch.',
|
||||||
sections: [
|
sections: [
|
||||||
{
|
{
|
||||||
tag: 'Signal 1 · Real-time event',
|
tag: 'Signal 1 · Real-time event',
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ export async function generateMetadata({ params }: OverviewPageProps): Promise<M
|
|||||||
const isZh = false // i18n shelved — Chinese branches kept as dead code for future revival; see messages/zh.json
|
const isZh = false // i18n shelved — Chinese branches kept as dead code for future revival; see messages/zh.json
|
||||||
|
|
||||||
const title = isZh
|
const title = isZh
|
||||||
? 'Trump Alpha 信号总览'
|
? 'Trump Alpha — 实时加密信号研究台'
|
||||||
: 'Trump Alpha Signal Monitor'
|
: 'Trump Alpha — Live Crypto Signal Desk'
|
||||||
const description = isZh
|
const description = isZh
|
||||||
? '实时查看 Trump Truth Social 信号、Macro Vibes 宏观氛围、KOL 观点和 talks-vs-trades 分歧,一页掌握核心加密信号。'
|
? 'Endorphin 研究台追踪四个先于市场共识的信号:Trump 帖子、BTC 宏观底部、资金费率极值,以及 KOL 言行偏离。每个信号公开且带时间戳。'
|
||||||
: 'Track Trump Truth Social signals, Macro Vibes (crypto macro regime), KOL calls, and talks-vs-trades divergence in one live crypto dashboard.'
|
: 'Endorphin tracks four signals that move crypto before the crowd: Trump posts, BTC macro bottoms, funding-rate extremes, and what KOLs do vs say. Public and timestamped.'
|
||||||
const path = `${siteUrl}/${locale}`
|
const path = `${siteUrl}/${locale}`
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
+14
-7
@@ -4,8 +4,8 @@ import Script from 'next/script'
|
|||||||
import './[locale]/globals.css'
|
import './[locale]/globals.css'
|
||||||
|
|
||||||
const siteTitle = 'Trump Alpha'
|
const siteTitle = 'Trump Alpha'
|
||||||
const siteTagline = 'Four crypto alpha feeds, one dashboard'
|
const siteTagline = 'The crypto signals that move price before the crowd'
|
||||||
const siteDescription = 'Real-time Trump Truth Social signals, BTC macro-bottom confluence, KOL Substack/podcast signals, and talks-vs-trades divergence — all scored by AI in one live dashboard.'
|
const siteDescription = "Endorphin is a crypto research desk tracking four signals that move markets before consensus catches up — Trump's posts, BTC macro bottoms, funding-rate extremes, and what KOLs do versus what they say. Every signal is public and timestamped."
|
||||||
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL
|
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -42,9 +42,9 @@ export const metadata: Metadata = {
|
|||||||
'funding rate reversal',
|
'funding rate reversal',
|
||||||
'Bitcoin cycle bottom',
|
'Bitcoin cycle bottom',
|
||||||
],
|
],
|
||||||
authors: [{ name: 'Trump Alpha' }],
|
authors: [{ name: 'Endorphin' }],
|
||||||
creator: 'Trump Alpha',
|
creator: 'Endorphin',
|
||||||
publisher: 'Trump Alpha',
|
publisher: 'Endorphin',
|
||||||
category: 'Finance',
|
category: 'Finance',
|
||||||
robots: {
|
robots: {
|
||||||
index: true,
|
index: true,
|
||||||
@@ -137,6 +137,8 @@ const jsonLd = {
|
|||||||
applicationSubCategory: 'Cryptocurrency Trading Tool',
|
applicationSubCategory: 'Cryptocurrency Trading Tool',
|
||||||
operatingSystem: 'Web',
|
operatingSystem: 'Web',
|
||||||
description: siteDescription,
|
description: siteDescription,
|
||||||
|
publisher: { '@id': `${_base}/#org` },
|
||||||
|
author: { '@id': `${_base}/#org` },
|
||||||
datePublished: '2025-01-01',
|
datePublished: '2025-01-01',
|
||||||
dateModified: _today,
|
dateModified: _today,
|
||||||
inLanguage: ['en'],
|
inLanguage: ['en'],
|
||||||
@@ -168,10 +170,15 @@ const jsonLd = {
|
|||||||
{
|
{
|
||||||
'@type': 'Organization',
|
'@type': 'Organization',
|
||||||
'@id': `${_base}/#org`,
|
'@id': `${_base}/#org`,
|
||||||
name: 'Trump Alpha',
|
name: 'Endorphin',
|
||||||
|
alternateName: ['Endorphin Research', 'Endorphin Desk'],
|
||||||
url: _base,
|
url: _base,
|
||||||
logo: `${_base}/icon`,
|
logo: `${_base}/icon`,
|
||||||
description: 'AI-powered crypto intelligence platform tracking Trump posts, on-chain signals, and KOL sentiment.',
|
description: 'Endorphin is a crypto research desk. Trump Alpha is its public signal project — four market-moving signals tracked live and timestamped.',
|
||||||
|
brand: {
|
||||||
|
'@type': 'Brand',
|
||||||
|
name: 'Trump Alpha',
|
||||||
|
},
|
||||||
contactPoint: {
|
contactPoint: {
|
||||||
'@type': 'ContactPoint',
|
'@type': 'ContactPoint',
|
||||||
email: 'support@bitnews.day',
|
email: 'support@bitnews.day',
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ export default function BotConfigPanel() {
|
|||||||
if (!address) return
|
if (!address) return
|
||||||
const ok = await confirmSign({
|
const ok = await confirmSign({
|
||||||
label: 'Switch to live trading',
|
label: 'Switch to live trading',
|
||||||
description: 'Your subscription will change from paper mode to live. No trade opens immediately — you still need to add a Hyperliquid API key and enable a signal module before the bot can act.',
|
description: 'Your subscription will change from paper mode to live. For your safety, Auto-Trade is turned OFF on this switch — you must re-enable it explicitly while live. No trade opens immediately; you still need to add a Hyperliquid API key first.',
|
||||||
danger: true,
|
danger: true,
|
||||||
})
|
})
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
# Trump Alpha
|
# Trump Alpha
|
||||||
|
|
||||||
> AI-powered crypto intelligence platform. Four uncorrelated signal sources: Trump Truth Social sentiment, Macro Vibes (crypto macro regime dashboard), KOL Substack/podcast signals, and talks-vs-trades on-chain divergence.
|
> A crypto research project by Endorphin, a research desk. Tracks four uncorrelated signals that move markets before consensus catches up: Trump Truth Social sentiment, Macro Vibes (crypto macro regime dashboard), KOL Substack/podcast signals, and talks-vs-trades on-chain divergence. Every signal is public and timestamped.
|
||||||
|
|
||||||
## What this site does
|
## What this site does
|
||||||
|
|
||||||
Trump Alpha aggregates four signal engines and one macro context view into one live dashboard. All four dashboards are free to read — no account required.
|
Trump Alpha is Endorphin's public signal desk: four independent signal engines and one macro context view in one live dashboard. All four dashboards are free to read — no account required, every call timestamped.
|
||||||
|
|
||||||
1. **Trump Truth Social Signal** — Scrapes Trump's Truth Social posts within 15 seconds of publish. AI classifies each post as LONG (bullish crypto), SHORT (bearish), or NOISE (off-topic) with a confidence score 0–100. Only posts scoring ≥ 88 confidence trigger the optional auto-trader. The entire pipeline from post to classification runs in under 3 seconds. Optional: auto-execute on Hyperliquid with isolated margin, automatic TP/SL, and six user-defined safety limits (leverage cap, position size, daily budget, active-hours window, confidence floor, stop-loss floor).
|
1. **Trump Truth Social Signal** — Scrapes Trump's Truth Social posts within 15 seconds of publish. AI classifies each post as LONG (bullish crypto), SHORT (bearish), or NOISE (off-topic) with a confidence score 0–100. Only posts scoring ≥ 88 confidence trigger the optional auto-trader. The entire pipeline from post to classification runs in under 3 seconds. Optional: auto-execute on Hyperliquid with isolated margin, automatic TP/SL, and six user-defined safety limits (leverage cap, position size, daily budget, active-hours window, confidence floor, stop-loss floor).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user