3393 lines
172 KiB
HTML
3393 lines
172 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="description" content="金融日报 — 北美华人投资者专属每日金融简报,由 Claude AI + Web Search 自动生成,每日两次更新">
|
||
<meta property="og:title" content="金融日报 | Financial Daily">
|
||
<meta property="og:description" content="北美华人投资者专属:全球指数·市场要闻·宏观数据·财报追踪·经济日历,每日自动更新">
|
||
<meta property="og:type" content="website">
|
||
<meta name="keywords" content="金融日报,北美投资,美股,A股,港股,宏观经济,财报,经济日历">
|
||
<meta name="theme-color" content="#070b14">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="apple-mobile-web-app-title" content="金融日报">
|
||
<link rel="manifest" href="manifest.json">
|
||
<title>金融日报 | Financial Daily — 北美华人投资者日报</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||
<style>
|
||
/* =========================================================
|
||
RESET & TOKENS
|
||
========================================================= */
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
:root {
|
||
--bg: #070b14;
|
||
--card: #0c1525;
|
||
--card2: #0f1c30;
|
||
--border: #192840;
|
||
--border-l: #22386a;
|
||
--accent: #00d4aa;
|
||
--up: #00e676;
|
||
--down: #ff5252;
|
||
--neutral: #4fc3f7;
|
||
--warn: #ffb300;
|
||
--text: #e2e8f0;
|
||
--muted: #7a8fa8;
|
||
--dimmed: #3a4e68;
|
||
--mono: 'JetBrains Mono','Fira Code',monospace;
|
||
--sans: 'Inter',system-ui,-apple-system,sans-serif;
|
||
--r: 8px;
|
||
--rl: 14px;
|
||
}
|
||
html { font-size: 14px; scroll-behavior: smooth; }
|
||
body { font-family: var(--sans); background: var(--bg); color: var(--text); line-height: 1.6; -webkit-font-smoothing: antialiased; }
|
||
a { color: var(--accent); text-decoration: none; }
|
||
a:hover { color: #33e0be; }
|
||
|
||
/* =========================================================
|
||
READING PROGRESS
|
||
========================================================= */
|
||
#reading-progress { position: fixed; top: 0; left: 0; height: 3px; width: 0; background: linear-gradient(90deg,var(--accent),#4fc3f7); z-index: 9999; transition: width .1s; }
|
||
|
||
/* =========================================================
|
||
HEADER
|
||
========================================================= */
|
||
.site-header {
|
||
position: sticky; top: 0; z-index: 100;
|
||
background: rgba(7,11,20,.96); backdrop-filter: blur(16px);
|
||
border-bottom: 1px solid var(--border);
|
||
padding: 10px 0;
|
||
}
|
||
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
||
.logo-block { display: flex; align-items: baseline; gap: 8px; }
|
||
.site-title { font-size: 1.1rem; font-weight: 800; color: var(--accent); letter-spacing: -.02em; }
|
||
.site-sub { font-size: .65rem; color: var(--muted); font-family: var(--mono); }
|
||
.header-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||
.header-date { font-family: var(--mono); font-size: .68rem; color: var(--muted); }
|
||
.header-badge { display: flex; align-items: center; gap: 5px; font-size: .62rem; color: var(--muted); background: rgba(0,212,170,.08); border: 1px solid rgba(0,212,170,.2); border-radius: 20px; padding: 2px 9px; }
|
||
.pulse-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: pulse 2s ease-in-out infinite; }
|
||
@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.4;transform:scale(.7)} }
|
||
.header-nav { display: flex; gap: 10px; }
|
||
.header-nav a { font-size: .68rem; color: var(--muted); }
|
||
.header-nav a:hover { color: var(--accent); }
|
||
.streak-badge { font-size: .62rem; font-family: var(--mono); background: rgba(255,179,0,.12); color: var(--warn); border: 1px solid rgba(255,179,0,.25); border-radius: 20px; padding: 2px 8px; }
|
||
|
||
/* =========================================================
|
||
NAV BAR
|
||
========================================================= */
|
||
.jump-nav {
|
||
position: sticky; top: 47px; z-index: 90;
|
||
background: rgba(7,11,20,.95); backdrop-filter: blur(14px);
|
||
border-bottom: 1px solid var(--border);
|
||
display: flex; overflow-x: auto; scrollbar-width: none;
|
||
}
|
||
.jump-nav::-webkit-scrollbar { display: none; }
|
||
.jump-nav a {
|
||
flex-shrink: 0; padding: 8px 14px; font-size: .72rem;
|
||
color: var(--muted); white-space: nowrap;
|
||
border-bottom: 2px solid transparent; transition: color .15s, border-color .15s;
|
||
}
|
||
.jump-nav a:hover { color: var(--text); text-decoration: none; }
|
||
.jump-nav a.active { color: var(--accent); border-bottom-color: var(--accent); }
|
||
.nav-sep { width: 1px; background: var(--border); margin: 8px 0; flex-shrink: 0; }
|
||
|
||
/* =========================================================
|
||
TICKER STRIP
|
||
========================================================= */
|
||
.indices-section { border-bottom: 1px solid var(--border); background: rgba(0,0,0,.2); }
|
||
.indices-scroll-wrapper { overflow: hidden; position: relative; }
|
||
.indices-track { display: flex; gap: 0; animation: ticker 60s linear infinite; width: max-content; }
|
||
.indices-track:hover { animation-play-state: paused; }
|
||
@keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
|
||
.ticker-card { padding: 8px 18px; border-right: 1px solid var(--border); white-space: nowrap; cursor: default; }
|
||
.ticker-name { font-size: .62rem; color: var(--muted); font-family: var(--mono); margin-bottom: 2px; }
|
||
.ticker-value { font-size: .9rem; font-weight: 700; font-family: var(--mono); }
|
||
.ticker-change { font-size: .68rem; font-family: var(--mono); }
|
||
.ticker-change.up,.ticker-value.up { color: var(--up); }
|
||
.ticker-change.down,.ticker-value.down { color: var(--down); }
|
||
.ticker-card.up .ticker-value { color: var(--text); }
|
||
.ticker-card.down .ticker-value { color: var(--text); }
|
||
|
||
/* =========================================================
|
||
LAYOUT
|
||
========================================================= */
|
||
.container { max-width: 1360px; margin: 0 auto; padding: 0 20px; }
|
||
.page-body { padding: 20px 0 40px; }
|
||
|
||
/* Primary grid: content + sidebar */
|
||
.content-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 380px;
|
||
gap: 20px;
|
||
align-items: start;
|
||
}
|
||
@media (max-width: 1024px) { .content-grid { grid-template-columns: 1fr; } }
|
||
|
||
/* 2-col within a column */
|
||
.two-up { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
|
||
@media (max-width: 640px) { .two-up { grid-template-columns: 1fr; } }
|
||
|
||
/* =========================================================
|
||
CARDS & SECTIONS
|
||
========================================================= */
|
||
.card {
|
||
background: var(--card); border: 1px solid var(--border);
|
||
border-radius: var(--rl); padding: 18px 20px;
|
||
transition: border-color .2s;
|
||
}
|
||
.section-label {
|
||
font-size: .65rem; font-family: var(--mono); color: var(--accent);
|
||
letter-spacing: .1em; text-transform: uppercase;
|
||
margin-bottom: 14px; padding-bottom: 10px;
|
||
border-bottom: 1px solid var(--border);
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.section-label::before { content:''; width:3px; height:12px; background:var(--accent); border-radius:2px; flex-shrink:0; }
|
||
.section-label .sec-count { margin-left:auto; color:var(--dimmed); font-size:.6rem; }
|
||
.sec-anchor { scroll-margin-top: 100px; }
|
||
.mb16 { margin-bottom: 16px; }
|
||
|
||
/* =========================================================
|
||
SUMMARY HERO
|
||
========================================================= */
|
||
.summary-section { padding: 16px 0 0; }
|
||
.summary-card {
|
||
border-radius: var(--rl); padding: 22px 26px;
|
||
border: 1px solid var(--border); margin-bottom: 16px;
|
||
}
|
||
.summary-card.bullish { background: linear-gradient(135deg,rgba(0,230,118,.07),rgba(0,212,170,.03)); border-color:rgba(0,230,118,.28); }
|
||
.summary-card.bearish { background: linear-gradient(135deg,rgba(255,82,82,.07),rgba(255,82,82,.02)); border-color:rgba(255,82,82,.28); }
|
||
.summary-card.neutral { background: linear-gradient(135deg,rgba(79,195,247,.06),rgba(0,212,170,.03)); border-color:rgba(79,195,247,.22); }
|
||
.summary-top { display:flex; align-items:center; gap:10px; margin-bottom:14px; flex-wrap:wrap; }
|
||
.sentiment-pill { font-size:.72rem; font-family:var(--mono); font-weight:700; padding:4px 14px; border-radius:20px; letter-spacing:.06em; }
|
||
.sentiment-pill.bullish { background:rgba(0,230,118,.15); color:var(--up); border:1px solid rgba(0,230,118,.3); }
|
||
.sentiment-pill.bearish { background:rgba(255,82,82,.15); color:var(--down); border:1px solid rgba(255,82,82,.3); }
|
||
.sentiment-pill.neutral { background:rgba(79,195,247,.12); color:var(--neutral);border:1px solid rgba(79,195,247,.25); }
|
||
.vix-badge { font-family:var(--mono); font-size:.72rem; color:var(--muted); }
|
||
.vix-num { color:var(--warn); font-weight:700; font-size:.9rem; }
|
||
.summary-headline { font-size:1.22rem; font-weight:800; color:var(--text); line-height:1.38; margin-bottom:14px; letter-spacing:-.02em; }
|
||
.key-points { list-style:none; display:flex; flex-direction:column; gap:7px; margin-bottom:14px; padding:0; }
|
||
.kp-item { font-size:.83rem; color:var(--text); padding:9px 14px; background:rgba(255,255,255,.04); border-radius:7px; border-left:3px solid var(--accent); display:flex; align-items:center; gap:8px; font-weight:500; line-height:1.4; }
|
||
.kp-arrow { color:var(--accent); font-size:.7rem; flex-shrink:0; }
|
||
.summary-details { margin-top:4px; }
|
||
.summary-details-toggle { font-size:.72rem; color:var(--muted); cursor:pointer; user-select:none; padding:4px 0; list-style:none; }
|
||
.summary-details-toggle:hover { color:var(--accent); }
|
||
.summary-context { font-size:.82rem; color:var(--muted); line-height:1.7; padding-top:8px; }
|
||
/* legacy hide */
|
||
.summary-icon,.summary-content,.summary-meta,.summary-label,.vix-display,.sentiment-badge { display:none !important; }
|
||
|
||
/* =========================================================
|
||
NEWS
|
||
========================================================= */
|
||
.news-item {
|
||
background: var(--card); border-radius: var(--r);
|
||
border: 1px solid var(--border); margin-bottom: 6px;
|
||
cursor: pointer; overflow: hidden; transition: border-color .18s;
|
||
}
|
||
.news-item:hover { border-color: var(--border-l); }
|
||
.news-item.expanded { border-color: var(--border-l); }
|
||
.news-header { display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px 9px; }
|
||
.imp-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink:0; margin-top:5px; }
|
||
.imp-dot.breaking { background:var(--down); box-shadow:0 0 5px rgba(255,82,82,.5); }
|
||
.imp-dot.major { background:var(--warn); }
|
||
.imp-dot.normal { background:var(--neutral); opacity:.35; }
|
||
.news-title { font-size:.86rem; font-weight:600; color:var(--text); line-height:1.42; flex:1; }
|
||
.news-title a { color:var(--text); }
|
||
.news-title a:hover { color:var(--accent); }
|
||
.news-expand-icon { color:var(--dimmed); flex-shrink:0; font-size:1rem; transition:transform .2s; }
|
||
.news-item.expanded .news-expand-icon { transform:rotate(45deg); color:var(--accent); }
|
||
.news-tldr { padding:0 14px 9px 31px; font-size:.76rem; color:var(--accent); line-height:1.5; }
|
||
.tldr-label { margin-right:3px; }
|
||
.news-body-collapse { display:none; padding:9px 14px 12px 31px; font-size:.79rem; color:var(--muted); line-height:1.65; border-top:1px solid var(--border); }
|
||
.news-item.expanded .news-body-collapse { display:block; }
|
||
.news-footer { display:flex; align-items:center; gap:7px; padding:7px 14px; flex-wrap:wrap; border-top:1px solid rgba(255,255,255,.035); }
|
||
.news-item.breaking { border-left:3px solid var(--down); }
|
||
.news-item.major { border-left:3px solid var(--warn); }
|
||
.news-item.normal { border-left:3px solid transparent; }
|
||
.importance-badge { font-size:.6rem; font-family:var(--mono); padding:2px 6px; border-radius:3px; }
|
||
.importance-badge.breaking { background:rgba(255,82,82,.12); color:var(--down); }
|
||
.importance-badge.major { background:rgba(255,179,0,.12); color:var(--warn); }
|
||
.importance-badge.normal { background:rgba(79,195,247,.08); color:var(--neutral); }
|
||
.new-badge { font-size:.58rem; font-family:var(--mono); padding:1px 5px; border-radius:3px; background:rgba(0,212,170,.15); color:var(--accent); border:1px solid rgba(0,212,170,.3); }
|
||
|
||
/* =========================================================
|
||
TAGS
|
||
========================================================= */
|
||
.tag { font-size:.6rem; font-family:var(--mono); padding:2px 6px; border-radius:3px; background:rgba(255,255,255,.06); color:var(--muted); }
|
||
|
||
/* =========================================================
|
||
LEADERS
|
||
========================================================= */
|
||
.leader-card { background:var(--bg); border:1px solid var(--border); border-radius:var(--r); padding:14px; margin-bottom:8px; transition:border-color .2s; }
|
||
.leader-card:hover { border-color:var(--border-l); }
|
||
.leader-header { display:flex; align-items:center; gap:10px; margin-bottom:10px; }
|
||
.leader-avatar { width:38px; height:38px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:.7rem; font-weight:700; font-family:var(--mono); flex-shrink:0; }
|
||
.leader-avatar.wb { background:linear-gradient(135deg,#4a3800,#8b6914); color:#ffd700; }
|
||
.leader-avatar.rd { background:linear-gradient(135deg,#1a3a5c,#2563a8); color:#60a5fa; }
|
||
.leader-avatar.jd { background:linear-gradient(135deg,#3a1a1a,#8b2525); color:#f87171; }
|
||
.leader-avatar.jp { background:linear-gradient(135deg,#1a3a2a,#256b40); color:#4ade80; }
|
||
.leader-avatar.default { background:rgba(0,212,170,.15); color:var(--accent); }
|
||
.leader-meta { flex:1; }
|
||
.leader-name { font-size:.83rem; font-weight:600; }
|
||
.leader-role { font-size:.68rem; color:var(--muted); }
|
||
.leader-date { font-size:.62rem; color:var(--dimmed); font-family:var(--mono); white-space:nowrap; }
|
||
.leader-quote { font-size:.82rem; color:var(--accent); font-style:italic; margin-bottom:8px; line-height:1.5; }
|
||
.leader-body { font-size:.78rem; color:var(--muted); line-height:1.6; margin-bottom:8px; }
|
||
|
||
/* =========================================================
|
||
MACRO
|
||
========================================================= */
|
||
.macro-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(200px,1fr)); gap:10px; }
|
||
.macro-card { background:var(--bg); border:1px solid var(--border); border-radius:var(--r); padding:12px 14px; transition:border-color .2s; }
|
||
.macro-card:hover { border-color:var(--border-l); }
|
||
.macro-card.dir-up { border-left:3px solid var(--up); }
|
||
.macro-card.dir-down{ border-left:3px solid var(--down); }
|
||
.macro-card.dir-neutral { border-left:3px solid var(--neutral); }
|
||
.macro-indicator { font-size:.65rem; font-family:var(--mono); color:var(--muted); margin-bottom:5px; text-transform:uppercase; }
|
||
.macro-value { font-size:1.1rem; font-weight:700; font-family:var(--mono); margin-bottom:4px; }
|
||
.macro-value-up { color:var(--up); }
|
||
.macro-value-down { color:var(--down); }
|
||
.macro-prev { font-size:.66rem; color:var(--dimmed); margin-bottom:5px; }
|
||
.macro-desc { font-size:.72rem; color:var(--muted); line-height:1.5; }
|
||
.arrow.up { color:var(--up); }
|
||
.arrow.down { color:var(--down); }
|
||
|
||
/* =========================================================
|
||
EARNINGS
|
||
========================================================= */
|
||
.earnings-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(200px,1fr)); gap:10px; }
|
||
.earnings-card { background:var(--bg); border:1px solid var(--border); border-radius:var(--r); padding:14px; transition:border-color .2s; }
|
||
.earnings-card:hover { border-color:var(--border-l); }
|
||
.earnings-card.beat { border-top:2px solid var(--up); }
|
||
.earnings-card.miss { border-top:2px solid var(--down); }
|
||
.earnings-card.pending{ border-top:2px solid var(--neutral); }
|
||
.e-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; }
|
||
.e-ticker { font-family:var(--mono); font-size:.85rem; font-weight:700; }
|
||
.e-company { font-size:.7rem; color:var(--muted); }
|
||
.e-badge { font-size:.6rem; font-family:var(--mono); padding:2px 7px; border-radius:3px; }
|
||
.e-badge.beat { background:rgba(0,230,118,.15); color:var(--up); }
|
||
.e-badge.miss { background:rgba(255,82,82,.15); color:var(--down); }
|
||
.e-badge.pending{ background:rgba(79,195,247,.1); color:var(--neutral); }
|
||
.earnings-eps { display:flex; gap:10px; margin-bottom:8px; }
|
||
.eps-item { flex:1; }
|
||
.eps-label { font-size:.6rem; font-family:var(--mono); color:var(--dimmed); margin-bottom:3px; }
|
||
.eps-value { font-size:.9rem; font-weight:700; font-family:var(--mono); }
|
||
.eps-value.actual.beat { color:var(--up); }
|
||
.eps-value.actual.miss { color:var(--down); }
|
||
.eps-value.est { color:var(--muted); }
|
||
.e-revenue { font-size:.72rem; color:var(--muted); margin-bottom:5px; }
|
||
.e-highlight { font-size:.72rem; color:var(--text); line-height:1.4; }
|
||
|
||
/* =========================================================
|
||
SECTORS
|
||
========================================================= */
|
||
.sectors-list { display:flex; flex-direction:column; gap:6px; }
|
||
.sector-row { display:grid; grid-template-columns:1fr auto 120px auto; gap:8px; align-items:center; padding:8px 10px; background:var(--bg); border-radius:var(--r); border:1px solid var(--border); font-size:.78rem; }
|
||
@media(max-width:480px){ .sector-row { grid-template-columns:1fr auto; } .sector-bar-wrap,.sector-etf{ display:none; } }
|
||
.sector-name { font-weight:500; }
|
||
.sector-name-en { font-size:.65rem; color:var(--muted); }
|
||
.sector-etf { font-family:var(--mono); font-size:.65rem; color:var(--dimmed); }
|
||
.sector-bar-wrap { height:5px; background:var(--border); border-radius:3px; overflow:hidden; }
|
||
.sector-bar { height:100%; border-radius:3px; }
|
||
.sector-bar.up { background:var(--up); }
|
||
.sector-bar.down { background:var(--down); }
|
||
.sector-pct { font-family:var(--mono); font-size:.78rem; font-weight:600; text-align:right; }
|
||
.sector-pct.up { color:var(--up); }
|
||
.sector-pct.down { color:var(--down); }
|
||
|
||
/* =========================================================
|
||
CENTRAL BANKS
|
||
========================================================= */
|
||
.cb-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
|
||
@media(max-width:600px){ .cb-grid { grid-template-columns:1fr; } }
|
||
.cb-card { background:var(--bg); border:1px solid var(--border); border-radius:var(--r); padding:14px; transition:border-color .2s; }
|
||
.cb-card:hover { border-color:var(--border-l); }
|
||
.cb-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:8px; gap:8px; flex-wrap:wrap; }
|
||
.cb-name { font-size:.82rem; font-weight:600; }
|
||
.cb-rate { font-family:var(--mono); font-size:.78rem; color:var(--accent); }
|
||
.cb-action { font-size:.75rem; color:var(--text); margin-bottom:6px; font-weight:500; }
|
||
.cb-note { font-size:.72rem; color:var(--muted); line-height:1.5; margin-bottom:8px; }
|
||
.cb-bias { font-size:.6rem; font-family:var(--mono); padding:2px 7px; border-radius:3px; }
|
||
.cb-bias.hawkish { background:rgba(255,82,82,.12); color:var(--down); }
|
||
.cb-bias.dovish { background:rgba(0,230,118,.12); color:var(--up); }
|
||
.cb-bias.neutral { background:rgba(79,195,247,.1); color:var(--neutral); }
|
||
.cb-next { font-size:.65rem; color:var(--dimmed); font-family:var(--mono); }
|
||
|
||
/* =========================================================
|
||
CALENDAR
|
||
========================================================= */
|
||
.calendar-grid { display:flex; flex-direction:column; gap:6px; }
|
||
.cal-item { background:var(--bg); border:1px solid var(--border); border-radius:var(--r); padding:10px 12px; display:flex; gap:12px; align-items:flex-start; border-left-width:3px; transition:border-color .2s; }
|
||
.cal-item.high { border-left-color:var(--down); }
|
||
.cal-item.medium { border-left-color:var(--warn); }
|
||
.cal-item.low { border-left-color:var(--neutral); }
|
||
.cal-date { font-family:var(--mono); font-size:.62rem; color:var(--muted); min-width:54px; line-height:1.5; }
|
||
.cal-event { font-size:.78rem; color:var(--text); font-weight:500; margin-bottom:3px; }
|
||
.cal-values { font-family:var(--mono); font-size:.64rem; color:var(--muted); }
|
||
.cal-values span { color:var(--neutral); }
|
||
|
||
/* =========================================================
|
||
AI WATCHLIST
|
||
========================================================= */
|
||
.watchlist-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px; }
|
||
.watchlist-card { background:var(--bg); border:1px solid var(--border); border-radius:var(--r); padding:14px; transition:border-color .18s,transform .18s; }
|
||
.watchlist-card:hover { border-color:var(--border-l); transform:translateY(-2px); }
|
||
.wc-header { display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:8px; }
|
||
.wc-ticker { font-family:var(--mono); font-size:.9rem; font-weight:700; color:var(--accent); }
|
||
.wc-name { font-size:.65rem; color:var(--muted); margin-top:1px; }
|
||
.wc-price { text-align:right; }
|
||
.wc-price-val { font-family:var(--mono); font-size:.85rem; font-weight:600; }
|
||
.wc-price-chg { font-size:.68rem; }
|
||
.wc-price-chg.up { color:var(--up); }
|
||
.wc-price-chg.down { color:var(--down); }
|
||
.wc-signal-row { display:flex; align-items:center; gap:8px; margin-bottom:7px; }
|
||
.signal-badge { font-family:var(--mono); font-size:.65rem; font-weight:700; padding:2px 9px; border-radius:4px; }
|
||
.signal-badge.BUY { background:rgba(0,230,118,.15); color:var(--up); border:1px solid rgba(0,230,118,.3); }
|
||
.signal-badge.SELL { background:rgba(255,82,82,.15); color:var(--down);border:1px solid rgba(255,82,82,.3); }
|
||
.signal-badge.HOLD { background:rgba(255,179,0,.15); color:var(--warn);border:1px solid rgba(255,179,0,.3); }
|
||
.wc-score { font-family:var(--mono); font-size:.68rem; color:var(--muted); }
|
||
.wc-score span { color:var(--text); font-weight:600; }
|
||
.wc-conclusion { font-size:.78rem; color:var(--text); margin-bottom:3px; font-weight:500; line-height:1.4; }
|
||
.wc-driver { font-size:.68rem; color:var(--muted); margin-bottom:8px; line-height:1.4; }
|
||
.wc-targets { display:flex; gap:6px; font-size:.66rem; margin-bottom:6px; flex-wrap:wrap; }
|
||
.wc-target-up { color:var(--up); background:rgba(0,230,118,.1); padding:2px 7px; border-radius:3px; }
|
||
.wc-target-down { color:var(--down); background:rgba(255,82,82,.1); padding:2px 7px; border-radius:3px; }
|
||
.risk-badge { font-size:.58rem; font-family:var(--mono); padding:2px 6px; border-radius:3px; text-transform:uppercase; }
|
||
.risk-badge.low { background:rgba(0,230,118,.12); color:var(--up); }
|
||
.risk-badge.medium { background:rgba(255,179,0,.12); color:var(--warn); }
|
||
.risk-badge.high { background:rgba(255,82,82,.12); color:var(--down); }
|
||
.wc-action { font-size:.68rem; color:var(--muted); line-height:1.4; margin-top:5px; }
|
||
|
||
/* =========================================================
|
||
MARKET DEBATE
|
||
========================================================= */
|
||
.debate-container { display:flex; flex-direction:column; gap:12px; }
|
||
.debate-header { font-family:var(--mono); font-size:.68rem; color:var(--muted); text-transform:uppercase; letter-spacing:.08em; }
|
||
.debate-sides { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
|
||
@media(max-width:600px){ .debate-sides { grid-template-columns:1fr; } }
|
||
.debate-side { background:var(--bg); border-radius:var(--r); padding:12px; }
|
||
.debate-side.bull { border-top:2px solid var(--up); }
|
||
.debate-side.bear { border-top:2px solid var(--down); }
|
||
.debate-side-title { font-size:.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.08em; margin-bottom:8px; }
|
||
.debate-side.bull .debate-side-title { color:var(--up); }
|
||
.debate-side.bear .debate-side-title { color:var(--down); }
|
||
.debate-point { display:flex; align-items:flex-start; gap:7px; margin-bottom:7px; font-size:.76rem; line-height:1.4; }
|
||
.conf-badge { font-size:.57rem; font-family:var(--mono); padding:1px 4px; border-radius:3px; flex-shrink:0; margin-top:2px; }
|
||
.conf-badge.high { background:rgba(79,195,247,.15); color:var(--neutral); }
|
||
.conf-badge.medium { background:rgba(255,179,0,.12); color:var(--warn); }
|
||
.debate-verdict { background:var(--bg); border-radius:var(--r); padding:12px 14px; border-left:3px solid var(--accent); }
|
||
.verdict-label { font-size:.62rem; font-family:var(--mono); text-transform:uppercase; letter-spacing:.1em; color:var(--muted); margin-bottom:5px; }
|
||
.verdict-text { font-size:.81rem; line-height:1.5; }
|
||
.verdict-lean { display:inline-block; margin-left:7px; font-size:.62rem; font-family:var(--mono); padding:2px 6px; border-radius:3px; }
|
||
.verdict-lean.bullish { background:rgba(0,230,118,.15); color:var(--up); }
|
||
.verdict-lean.bearish { background:rgba(255,82,82,.15); color:var(--down); }
|
||
.verdict-lean.neutral { background:rgba(79,195,247,.12); color:var(--neutral); }
|
||
|
||
/* =========================================================
|
||
MARKET REPLAY
|
||
========================================================= */
|
||
.replay-grid { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
|
||
@media(max-width:600px){ .replay-grid { grid-template-columns:1fr; } }
|
||
.replay-card { background:var(--bg); border:1px solid var(--border); border-radius:var(--r); padding:14px; }
|
||
.replay-card-title { font-size:.65rem; font-family:var(--mono); text-transform:uppercase; letter-spacing:.1em; color:var(--accent); margin-bottom:5px; }
|
||
.replay-summary { font-size:.82rem; font-weight:500; margin-bottom:10px; line-height:1.4; }
|
||
.ad-bar-wrap { display:flex; align-items:center; gap:6px; margin-bottom:8px; font-size:.7rem; }
|
||
.ad-bar { height:5px; border-radius:3px; }
|
||
.ad-bar.adv { background:var(--up); }
|
||
.ad-bar.dec { background:var(--down); }
|
||
.ad-label-up { color:var(--up); min-width:45px; font-family:var(--mono); font-size:.68rem; }
|
||
.ad-label-dn { color:var(--down); min-width:45px; font-family:var(--mono); font-size:.68rem; }
|
||
.replay-stats { display:grid; grid-template-columns:1fr 1fr 1fr; gap:5px; }
|
||
.replay-stat { background:rgba(255,255,255,.025); border-radius:5px; padding:6px 8px; }
|
||
.replay-stat-label { font-size:.58rem; color:var(--muted); margin-bottom:1px; }
|
||
.replay-stat-val { font-size:.76rem; font-weight:600; font-family:var(--mono); }
|
||
|
||
/* =========================================================
|
||
PERSONAL / MY DASHBOARD (collapsible drawer)
|
||
========================================================= */
|
||
.my-drawer { margin-top: 20px; }
|
||
.my-drawer-toggle {
|
||
width:100%; padding:11px 16px; background:var(--card); border:1px solid var(--border);
|
||
border-radius:var(--r); color:var(--muted); font-size:.78rem; cursor:pointer;
|
||
display:flex; align-items:center; justify-content:space-between; font-family:var(--sans);
|
||
transition:border-color .18s;
|
||
}
|
||
.my-drawer-toggle:hover { border-color:var(--border-l); color:var(--text); }
|
||
.my-drawer-toggle .drawer-icon { transition:transform .25s; }
|
||
.my-drawer-toggle.open .drawer-icon { transform:rotate(180deg); }
|
||
.my-drawer-body { display:none; margin-top:8px; }
|
||
.my-drawer-body.open { display:block; }
|
||
|
||
/* =========================================================
|
||
SUBSCRIBE STRIP (compact)
|
||
========================================================= */
|
||
.subscribe-strip {
|
||
background: rgba(0,212,170,.05); border: 1px solid rgba(0,212,170,.15);
|
||
border-radius: var(--r); padding: 12px 16px;
|
||
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
||
margin-bottom: 16px;
|
||
}
|
||
.subscribe-strip-icon { font-size: 1.1rem; flex-shrink: 0; }
|
||
.subscribe-strip-body { flex: 1; min-width: 160px; }
|
||
.subscribe-strip-body strong { display:block; color:var(--accent); font-size:.82rem; }
|
||
.subscribe-strip-body span { font-size:.7rem; color:var(--muted); }
|
||
.subscribe-strip-actions { display:flex; gap:7px; flex-wrap:wrap; align-items:center; }
|
||
.btn-sub-email, .btn-sub-tg {
|
||
font-size:.72rem; padding:6px 12px; border-radius:var(--r); cursor:pointer;
|
||
display:flex; align-items:center; gap:5px; white-space:nowrap;
|
||
}
|
||
.btn-sub-email { background:rgba(0,212,170,.15); color:var(--accent); border:1px solid rgba(0,212,170,.3); }
|
||
.btn-sub-tg { background:rgba(79,195,247,.12); color:var(--neutral); border:1px solid rgba(79,195,247,.25); text-decoration:none; }
|
||
|
||
/* =========================================================
|
||
SIMPLE MODE
|
||
========================================================= */
|
||
body.simple-mode .card-secondary { display: none; }
|
||
body.simple-mode .news-item:not(.featured) { display: none; }
|
||
|
||
/* =========================================================
|
||
FAB CLUSTER
|
||
========================================================= */
|
||
.fab-cluster { position:fixed; bottom:20px; right:16px; display:flex; flex-direction:column; gap:8px; z-index:500; }
|
||
.fab { width:40px; height:40px; border-radius:50%; background:var(--card); border:1px solid var(--border); color:var(--text); font-size:1rem; cursor:pointer; display:flex; align-items:center; justify-content:center; transition:border-color .18s; box-shadow:0 4px 16px rgba(0,0,0,.4); }
|
||
.fab:hover { border-color:var(--border-l); }
|
||
#share-toast { position:fixed; bottom:70px; right:16px; background:var(--card); border:1px solid var(--border); border-radius:var(--r); padding:8px 14px; font-size:.75rem; color:var(--accent); display:none; z-index:600; }
|
||
@media(max-width:640px){ .fab-cluster { bottom:70px; } }
|
||
|
||
/* =========================================================
|
||
FOOTER
|
||
========================================================= */
|
||
.site-footer { border-top:1px solid var(--border); padding:20px 0; margin-top:20px; }
|
||
.footer-inner { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; font-size:.7rem; color:var(--muted); }
|
||
|
||
/* =========================================================
|
||
ANIMATIONS
|
||
========================================================= */
|
||
.sec-anchor { animation: fadeUp .3s ease both; }
|
||
@keyframes fadeUp { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:none} }
|
||
|
||
@media (max-width: 640px) {
|
||
.jump-nav { position:fixed; bottom:0; top:auto; left:0; right:0; border-bottom:none; border-top:1px solid var(--border); justify-content:space-around; padding-bottom:env(safe-area-inset-bottom); z-index:200; }
|
||
.jump-nav a { padding:7px 3px; font-size:.58rem; flex-direction:column; gap:1px; text-align:center; }
|
||
body { padding-bottom: 60px; }
|
||
}
|
||
|
||
/* =========================================================
|
||
TRADINGVIEW OVERRIDE
|
||
========================================================= */
|
||
.tradingview-widget-container { border-radius:var(--r); overflow:hidden; }
|
||
.section-block { margin-bottom: 16px; }
|
||
|
||
/* =========================================================
|
||
INTRO OVERLAY ANIMATION
|
||
========================================================= */
|
||
#intro-overlay {
|
||
position: fixed; inset: 0; z-index: 10000;
|
||
background: #030609;
|
||
display: flex; align-items: center; justify-content: center;
|
||
flex-direction: column; overflow: hidden;
|
||
transition: opacity .7s ease;
|
||
}
|
||
#intro-overlay.fade-out { opacity: 0; pointer-events: none; }
|
||
#intro-overlay.gone { display: none; }
|
||
#intro-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||
#intro-content {
|
||
position: relative; z-index: 2;
|
||
display: flex; flex-direction: column; align-items: center; gap: 18px;
|
||
text-align: center; padding: 0 20px;
|
||
}
|
||
.intro-badge {
|
||
font-size: .62rem; font-family: var(--mono); letter-spacing: .15em;
|
||
color: var(--accent); border: 1px solid rgba(0,212,170,.3);
|
||
background: rgba(0,212,170,.08); padding: 4px 14px; border-radius: 20px;
|
||
animation: fadeUp .6s .4s both;
|
||
}
|
||
.intro-logo {
|
||
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
||
animation: fadeUp .7s .7s both;
|
||
}
|
||
.intro-title-zh {
|
||
font-size: clamp(2.2rem, 6vw, 4rem); font-weight: 900;
|
||
background: linear-gradient(135deg, #00d4aa, #4fc3f7, #00d4aa);
|
||
background-size: 200% 200%;
|
||
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
||
background-clip: text; animation: shimmer 3s ease infinite;
|
||
letter-spacing: -.03em;
|
||
}
|
||
.intro-title-en {
|
||
font-size: clamp(.9rem, 2.5vw, 1.4rem); font-weight: 300;
|
||
color: rgba(226,232,240,.55); letter-spacing: .25em;
|
||
text-transform: uppercase; font-family: var(--mono);
|
||
}
|
||
@keyframes shimmer {
|
||
0% { background-position: 0% 50%; }
|
||
50% { background-position: 100% 50%; }
|
||
100% { background-position: 0% 50%; }
|
||
}
|
||
.intro-sub {
|
||
font-size: .7rem; color: var(--muted); font-family: var(--mono);
|
||
letter-spacing: .08em;
|
||
animation: fadeUp .6s 1s both;
|
||
}
|
||
.intro-ticker-row {
|
||
display: flex; gap: 18px; flex-wrap: wrap; justify-content: center;
|
||
animation: fadeUp .6s 1.2s both;
|
||
}
|
||
.intro-tick {
|
||
font-family: var(--mono); font-size: .72rem;
|
||
background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08);
|
||
border-radius: 6px; padding: 5px 12px; white-space: nowrap;
|
||
}
|
||
.intro-tick .it-name { color: var(--muted); font-size: .58rem; display: block; margin-bottom: 2px; }
|
||
.intro-tick .it-val { color: var(--text); font-weight: 600; }
|
||
.intro-tick .it-chg { margin-left: 6px; font-size: .65rem; }
|
||
.intro-tick .it-chg.up { color: var(--up); }
|
||
.intro-tick .it-chg.down { color: var(--down); }
|
||
#intro-skip {
|
||
font-size: .7rem; color: var(--muted); background: rgba(255,255,255,.04);
|
||
border: 1px solid rgba(255,255,255,.1); border-radius: 20px;
|
||
padding: 6px 18px; cursor: pointer; font-family: var(--sans);
|
||
transition: color .15s, border-color .15s;
|
||
animation: fadeUp .5s 1.6s both;
|
||
}
|
||
#intro-skip:hover { color: var(--accent); border-color: rgba(0,212,170,.4); }
|
||
#intro-progress {
|
||
position: absolute; bottom: 0; left: 0; height: 2px;
|
||
background: linear-gradient(90deg, var(--accent), #4fc3f7);
|
||
width: 0%; transition: width linear;
|
||
}
|
||
|
||
/* =========================================================
|
||
LANG TOGGLE
|
||
========================================================= */
|
||
#lang-btn {
|
||
display: flex; align-items: center; gap: 0;
|
||
background: rgba(255,255,255,.04); border: 1px solid var(--border);
|
||
border-radius: 20px; overflow: hidden; cursor: pointer;
|
||
font-size: .62rem; font-family: var(--mono);
|
||
transition: border-color .15s;
|
||
flex-shrink: 0;
|
||
}
|
||
#lang-btn:hover { border-color: var(--border-l); }
|
||
.lang-opt {
|
||
padding: 3px 9px; transition: background .15s, color .15s;
|
||
color: var(--muted);
|
||
}
|
||
.lang-opt.active { background: rgba(0,212,170,.15); color: var(--accent); }
|
||
|
||
/* =========================================================
|
||
I18N: hide/show per lang
|
||
========================================================= */
|
||
body.lang-en .zh-only { display: none !important; }
|
||
body.lang-zh .en-only { display: none !important; }
|
||
|
||
/* =========================================================
|
||
HIDE GOOGLE TRANSLATE CHROME (keep translation, drop UI)
|
||
========================================================= */
|
||
.goog-te-banner-frame,
|
||
.goog-te-balloon-frame,
|
||
#goog-gt-tt,
|
||
.goog-te-balloon-frame,
|
||
.goog-tooltip,
|
||
.goog-tooltip:hover,
|
||
.goog-text-highlight { display: none !important; }
|
||
.skiptranslate { display: none !important; }
|
||
body { top: 0 !important; }
|
||
iframe.goog-te-menu-frame { display: none !important; }
|
||
/* Prevent GT from injecting font-size changes on html */
|
||
html.translated-ltr, html.translated-rtl { margin-top: 0 !important; }
|
||
</style>
|
||
|
||
<!-- Google Translate hidden widget -->
|
||
<div id="google_translate_element" style="display:none;visibility:hidden;position:absolute;width:0;height:0;"></div>
|
||
<script>
|
||
function googleTranslateElementInit() {
|
||
new google.translate.TranslateElement({
|
||
pageLanguage: 'zh-CN',
|
||
includedLanguages: 'en,zh-CN',
|
||
autoDisplay: false,
|
||
gaTrack: false
|
||
}, 'google_translate_element');
|
||
}
|
||
</script>
|
||
<script src="https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit" defer></script>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- =========================================================
|
||
INTRO OVERLAY
|
||
========================================================= -->
|
||
<div id="intro-overlay">
|
||
<canvas id="intro-canvas"></canvas>
|
||
<div id="intro-content">
|
||
<div class="intro-badge">AI-POWERED · NORTH AMERICA MARKETS</div>
|
||
<div class="intro-logo">
|
||
<span class="intro-title-zh">金融日报</span>
|
||
<span class="intro-title-en">Financial Daily</span>
|
||
</div>
|
||
<div class="intro-sub" id="intro-sub">北美华人投资者 · 每日智能金融简报</div>
|
||
<div class="intro-ticker-row" id="intro-tickers">
|
||
<div class="intro-tick"><span class="it-name">S&P 500</span><span class="it-val" id="it-spy">—</span><span class="it-chg" id="it-spy-chg"></span></div>
|
||
<div class="intro-tick"><span class="it-name">NASDAQ</span><span class="it-val" id="it-qqq">—</span><span class="it-chg" id="it-qqq-chg"></span></div>
|
||
<div class="intro-tick"><span class="it-name">BTC</span><span class="it-val" id="it-btc">—</span><span class="it-chg" id="it-btc-chg"></span></div>
|
||
<div class="intro-tick"><span class="it-name">GOLD</span><span class="it-val" id="it-gld">—</span><span class="it-chg" id="it-gld-chg"></span></div>
|
||
</div>
|
||
<button id="intro-skip" onclick="dismissIntro()">进入 · Enter →</button>
|
||
</div>
|
||
<div id="intro-progress"></div>
|
||
</div>
|
||
|
||
<div id="reading-progress"></div>
|
||
|
||
<!-- HEADER -->
|
||
<header class="site-header">
|
||
<div class="container">
|
||
<div class="header-inner">
|
||
<div class="logo-block">
|
||
<span class="site-title">金融日报</span>
|
||
<span class="site-sub">Financial Daily</span>
|
||
</div>
|
||
<div class="header-meta">
|
||
<span class="header-date" id="current-date">Loading...</span>
|
||
<div class="header-badge"><span class="pulse-dot"></span>Claude AI</div>
|
||
<nav class="header-nav">
|
||
<a href="../index.html" data-i18n="nav-archive">归档</a>
|
||
<a href="../feed.xml" style="color:var(--dimmed)">RSS</a>
|
||
</nav>
|
||
<button id="lang-btn" onclick="toggleLang()" title="切换语言 / Switch Language">
|
||
<span class="lang-opt" id="lang-zh">ZH</span>
|
||
<span class="lang-opt" id="lang-en">EN</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- NAV -->
|
||
<nav class="jump-nav" id="jump-nav">
|
||
<a href="#sec-summary" data-i18n="nav-summary">📊 大势</a>
|
||
<a href="#sec-ai-trading" data-i18n="nav-ai-trading">🤖 AI操盘</a>
|
||
<a href="#sec-news" data-i18n="nav-news">📰 要闻</a>
|
||
<a href="#sec-watchlist" data-i18n="nav-watchlist">🎯 AI分析</a>
|
||
<a href="#sec-macro" data-i18n="nav-macro">📈 宏观</a>
|
||
<a href="#sec-earnings" data-i18n="nav-earnings">💼 财报</a>
|
||
<a href="#sec-sectors" data-i18n="nav-sectors">🏭 行业</a>
|
||
<a href="#sec-debate" data-i18n="nav-debate">⚔️ 辩论</a>
|
||
<a href="#sec-calendar" data-i18n="nav-calendar">📅 日历</a>
|
||
<a href="#sec-my" data-i18n="nav-my">⭐ 我的</a>
|
||
</nav>
|
||
|
||
<!-- INDICES TICKER -->
|
||
<section class="indices-section">
|
||
<div class="indices-scroll-wrapper">
|
||
<div class="indices-track">
|
||
<!-- INDICES:START -->
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">S&P 500</div>
|
||
<div class="ticker-value">7,575.39</div>
|
||
<div class="ticker-change up">▲ +0.42%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">纳斯达克综合指数</div>
|
||
<div class="ticker-value">26,281.61</div>
|
||
<div class="ticker-change up">▲ +0.29%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">道琼斯工业指数</div>
|
||
<div class="ticker-value">52,637.01</div>
|
||
<div class="ticker-change up">▲ +0.29%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">上证综指</div>
|
||
<div class="ticker-value">3,996.16</div>
|
||
<div class="ticker-change down">▼ -1.00%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">恒生指数</div>
|
||
<div class="ticker-value">24,175.12</div>
|
||
<div class="ticker-change up">▲ +0.60%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">日经225</div>
|
||
<div class="ticker-value">68,557.73</div>
|
||
<div class="ticker-change up">▲ +1.20%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">德国DAX</div>
|
||
<div class="ticker-value">未获取</div>
|
||
<div class="ticker-change up">▲ +0.85%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">黄金现货</div>
|
||
<div class="ticker-value">4,128.90</div>
|
||
<div class="ticker-change up">▲ +0.37%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">WTI原油</div>
|
||
<div class="ticker-value">71.46</div>
|
||
<div class="ticker-change down">▼ -0.86%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">比特币</div>
|
||
<div class="ticker-value">63,848.09</div>
|
||
<div class="ticker-change up">▲ +1.08%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">美元指数</div>
|
||
<div class="ticker-value">未获取具体值</div>
|
||
<div class="ticker-change up">▲ +0.24%</div>
|
||
</div>
|
||
<div class="ticker-card neutral">
|
||
<div class="ticker-name">离岸人民币</div>
|
||
<div class="ticker-value">未获取</div>
|
||
<div class="ticker-change neutral">– N/A</div>
|
||
</div>
|
||
<!-- INDICES:END -->
|
||
<!-- INDICES:END -->
|
||
<!-- duplicate for seamless loop -->
|
||
<!-- INDICES:START -->
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">S&P 500</div>
|
||
<div class="ticker-value">7,575.39</div>
|
||
<div class="ticker-change up">▲ +0.42%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">纳斯达克综合指数</div>
|
||
<div class="ticker-value">26,281.61</div>
|
||
<div class="ticker-change up">▲ +0.29%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">道琼斯工业指数</div>
|
||
<div class="ticker-value">52,637.01</div>
|
||
<div class="ticker-change up">▲ +0.29%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">上证综指</div>
|
||
<div class="ticker-value">3,996.16</div>
|
||
<div class="ticker-change down">▼ -1.00%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">恒生指数</div>
|
||
<div class="ticker-value">24,175.12</div>
|
||
<div class="ticker-change up">▲ +0.60%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">日经225</div>
|
||
<div class="ticker-value">68,557.73</div>
|
||
<div class="ticker-change up">▲ +1.20%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">德国DAX</div>
|
||
<div class="ticker-value">未获取</div>
|
||
<div class="ticker-change up">▲ +0.85%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">黄金现货</div>
|
||
<div class="ticker-value">4,128.90</div>
|
||
<div class="ticker-change up">▲ +0.37%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">WTI原油</div>
|
||
<div class="ticker-value">71.46</div>
|
||
<div class="ticker-change down">▼ -0.86%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">比特币</div>
|
||
<div class="ticker-value">63,848.09</div>
|
||
<div class="ticker-change up">▲ +1.08%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">美元指数</div>
|
||
<div class="ticker-value">未获取具体值</div>
|
||
<div class="ticker-change up">▲ +0.24%</div>
|
||
</div>
|
||
<div class="ticker-card neutral">
|
||
<div class="ticker-name">离岸人民币</div>
|
||
<div class="ticker-value">未获取</div>
|
||
<div class="ticker-change neutral">– N/A</div>
|
||
</div>
|
||
<!-- INDICES:END -->
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- SUMMARY HERO -->
|
||
<section class="summary-section sec-anchor" id="sec-summary">
|
||
<div class="container">
|
||
<!-- SUMMARY:START -->
|
||
<div class="summary-card neutral">
|
||
<div class="summary-top">
|
||
<span class="sentiment-pill neutral">🟡 中性 · Neutral</span>
|
||
<span class="vix-badge">VIX <span class="vix-num">15.03</span></span>
|
||
</div>
|
||
<div class="summary-headline">美股震荡上扬,AI芯片领涨,伊朗停火缓解油价压力</div>
|
||
<ul class="key-points"><li class="kp-item"><span class="kp-arrow">▸</span>SK Hynix首日涨13%引爆AI内存题材</li><li class="kp-item"><span class="kp-arrow">▸</span>美联储9月加息概率升至58%</li><li class="kp-item"><span class="kp-arrow">▸</span>中美关税谈判现曙光但风险犹存</li></ul>
|
||
<details class="summary-details">
|
||
<summary class="summary-details-toggle">深度解读 ▸</summary>
|
||
<div class="summary-context">标普500周五收涨0.42%至7,575点,纳指涨0.29%,道指涨149点。SK Hynix美国上市首日暴涨13%,Meta单周飙升15%创2024年来最佳表现。VIX恐慌指数回落至15.03。美联储7月会议临近,市场定价显示利率维持3.5-3.75%概率最高,但通胀粘性令9月加息概率升至58%。中美农产品关税削减谈判取得进展,但强制劳动调查可能对华加征12.5%新关税。对北美华人投资者而言:科技股短期承压但AI需求强劲,美元走强压制新兴市场,需警惕Fed鹰派转向风险。</div>
|
||
</details>
|
||
</div>
|
||
<!-- SUMMARY:END -->
|
||
<!-- SUMMARY:END -->
|
||
</div>
|
||
</section>
|
||
|
||
<!-- PODCAST PLAYER -->
|
||
<div class="podcast-bar" id="podcast-bar">
|
||
<div class="container">
|
||
<div class="podcast-inner">
|
||
<div class="podcast-left">
|
||
<span class="podcast-icon">🎙️</span>
|
||
<div class="podcast-info">
|
||
<div class="podcast-title">金融日报早报 · 今日播客</div>
|
||
<div class="podcast-sub" id="podcast-date">欣欣 & 建国 · 约3分钟</div>
|
||
</div>
|
||
</div>
|
||
<div class="podcast-controls">
|
||
<audio id="podcast-audio" preload="none">
|
||
<source src="audio/latest.mp3" type="audio/mpeg">
|
||
</audio>
|
||
<button class="pod-btn" id="pod-play" onclick="togglePodcast()">▶ 播放</button>
|
||
<div class="pod-progress-wrap" onclick="seekPodcast(event)">
|
||
<div class="pod-progress-bar" id="pod-progress"></div>
|
||
</div>
|
||
<span class="pod-time" id="pod-time">0:00</span>
|
||
<button class="pod-btn pod-btn-sm" onclick="document.getElementById('podcast-audio').playbackRate=document.getElementById('podcast-audio').playbackRate===1?1.5:1;this.textContent=document.getElementById('podcast-audio').playbackRate+'x'">1x</button>
|
||
<a class="pod-btn pod-btn-sm" href="audio/latest.mp3" download="金融日报早报.mp3" title="下载">⬇</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.podcast-bar {
|
||
background: linear-gradient(90deg, rgba(0,212,170,.08), rgba(79,195,247,.05));
|
||
border-bottom: 1px solid rgba(0,212,170,.2);
|
||
padding: 10px 0;
|
||
}
|
||
.podcast-inner { display:flex; align-items:center; justify-content:space-between; gap:14px; flex-wrap:wrap; }
|
||
.podcast-left { display:flex; align-items:center; gap:10px; }
|
||
.podcast-icon { font-size:1.4rem; animation: pulse-pod 3s ease-in-out infinite; }
|
||
@keyframes pulse-pod { 0%,100%{transform:scale(1)} 50%{transform:scale(1.1)} }
|
||
.podcast-title { font-size:.82rem; font-weight:600; color:var(--text); }
|
||
.podcast-sub { font-size:.65rem; color:var(--muted); font-family:var(--mono); margin-top:1px; }
|
||
.podcast-controls { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
|
||
.pod-btn {
|
||
background: rgba(0,212,170,.15); color:var(--accent);
|
||
border: 1px solid rgba(0,212,170,.3); border-radius:6px;
|
||
padding: 5px 14px; font-size:.75rem; cursor:pointer;
|
||
font-family:var(--sans); transition:background .15s; white-space:nowrap;
|
||
}
|
||
.pod-btn:hover { background:rgba(0,212,170,.25); }
|
||
.pod-btn.playing { background:rgba(255,82,82,.12); color:var(--down); border-color:rgba(255,82,82,.3); }
|
||
.pod-btn-sm { padding:5px 9px; font-size:.68rem; }
|
||
.pod-btn-sm[download] { color:var(--muted); background:rgba(255,255,255,.05); border-color:var(--border); text-decoration:none; }
|
||
.pod-progress-wrap { width:160px; height:4px; background:var(--border); border-radius:2px; cursor:pointer; }
|
||
@media(max-width:640px){ .pod-progress-wrap { width:80px; } }
|
||
.pod-progress-bar { height:100%; background:var(--accent); border-radius:2px; width:0%; transition:width .3s linear; }
|
||
.pod-time { font-family:var(--mono); font-size:.65rem; color:var(--muted); min-width:32px; }
|
||
</style>
|
||
|
||
<script>
|
||
(function(){
|
||
var audio = document.getElementById('podcast-audio');
|
||
var btn = document.getElementById('pod-play');
|
||
var bar = document.getElementById('pod-progress');
|
||
var time = document.getElementById('pod-time');
|
||
if (!audio) return;
|
||
|
||
// Check if audio exists
|
||
fetch('audio/latest-meta.json').then(r=>r.json()).then(m=>{
|
||
document.getElementById('podcast-date').textContent = '欣欣 & 建国 · ' + m.date + ' · 约3分钟';
|
||
}).catch(()=>{
|
||
document.getElementById('podcast-bar').style.opacity = '0.4';
|
||
btn.textContent = '暂无音频';
|
||
btn.disabled = true;
|
||
});
|
||
|
||
audio.addEventListener('timeupdate', function(){
|
||
if (!audio.duration) return;
|
||
bar.style.width = (audio.currentTime / audio.duration * 100) + '%';
|
||
var m = Math.floor(audio.currentTime/60);
|
||
var s = Math.floor(audio.currentTime%60);
|
||
time.textContent = m + ':' + (s<10?'0':'')+s;
|
||
});
|
||
audio.addEventListener('ended', function(){
|
||
btn.textContent = '▶ 播放';
|
||
btn.classList.remove('playing');
|
||
bar.style.width = '0%';
|
||
});
|
||
|
||
window.togglePodcast = function(){
|
||
if (audio.paused) {
|
||
audio.play();
|
||
btn.textContent = '⏸ 暂停';
|
||
btn.classList.add('playing');
|
||
} else {
|
||
audio.pause();
|
||
btn.textContent = '▶ 播放';
|
||
btn.classList.remove('playing');
|
||
}
|
||
};
|
||
window.seekPodcast = function(e){
|
||
if (!audio.duration) return;
|
||
var rect = e.currentTarget.getBoundingClientRect();
|
||
audio.currentTime = ((e.clientX - rect.left) / rect.width) * audio.duration;
|
||
};
|
||
})();
|
||
</script>
|
||
|
||
<!-- MAIN PAGE BODY -->
|
||
<div class="page-body">
|
||
<div class="container">
|
||
|
||
<!-- SUBSCRIBE (dismissible) -->
|
||
<div class="subscribe-strip" id="subscribe-strip">
|
||
<div class="subscribe-strip-icon">📬</div>
|
||
<div class="subscribe-strip-body">
|
||
<strong>订阅每日金融早报</strong>
|
||
<span>每个交易日自动更新,开盘前 + 收盘后各一次</span>
|
||
</div>
|
||
<div class="subscribe-strip-actions">
|
||
<form action="https://buttondown.email/api/emails/embed-subscribe/yangbai0110" method="post" target="_blank" style="display:flex;gap:6px;">
|
||
<input type="email" name="email" placeholder="your@email.com" required style="padding:6px 10px;border-radius:6px;border:1px solid var(--border);background:rgba(255,255,255,.05);color:var(--text);font-size:.73rem;width:148px;outline:none;" />
|
||
<button type="submit" class="btn-sub-email">📧 邮件</button>
|
||
</form>
|
||
<a href="https://t.me/GGmanm_bot?start=subscribe" target="_blank" class="btn-sub-tg">
|
||
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.894 8.221-1.97 9.28c-.145.658-.537.818-1.084.508l-3-2.21-1.447 1.394c-.16.16-.295.295-.605.295l.213-3.053 5.56-5.023c.242-.213-.054-.333-.373-.12L7.44 13.55l-2.955-.924c-.643-.204-.657-.643.136-.953l11.566-4.461c.537-.194 1.006.131.707.009z"/></svg>
|
||
Telegram
|
||
</a>
|
||
<button onclick="document.getElementById('subscribe-strip').style.display='none';localStorage.setItem('fd_sub_dismissed','1');" style="background:none;border:none;color:var(--dimmed);cursor:pointer;font-size:1rem;padding:2px 4px;" title="关闭">×</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- PRIMARY GRID: main content + sidebar -->
|
||
<div class="content-grid">
|
||
|
||
<!-- ── LEFT / MAIN COLUMN ─────────────────────────────── -->
|
||
<div class="main-col">
|
||
|
||
<!-- NEWS -->
|
||
<div class="section-block sec-anchor" id="sec-news">
|
||
<div class="card">
|
||
<div class="section-label">📰 市场要闻 · Market News</div>
|
||
<!-- NEWS:START -->
|
||
<div class="news-item breaking featured" onclick="toggleNews(this)">
|
||
<div class="news-header">
|
||
<span class="imp-dot breaking"></span>
|
||
<div class="news-title">SK Hynix美国上市首日暴涨13%,募资265亿美元创外企IPO纪录</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>持有美光(MU)的投资者需关注竞争加剧,但HBM整体需求强劲,AI芯片板块仍是主赛道</div>
|
||
<div class="news-body-collapse">韩国存储芯片巨头SK Hynix周五在纳斯达克首日交易,股票代码SKHYV,发行价149美元,开盘价170美元,收盘涨约13%。此次IPO募资265亿美元,成为史上最大外企美国上市案。AI驱动的高带宽内存(HBM)需求激增,SK Hynix与三星、美光形成三强格局,纳斯达克总裁称'需求将是巨大的'。上市首日带动Nvidia涨4%、AMD涨2%,但也引发对分流美光等美国存储股资金的担忧。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge breaking">🔥 BREAKING</span>
|
||
<div class="tags"><span class="tag">#AI芯片</span><span class="tag">#IPO</span><span class="tag">#存储</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="news-item breaking featured" onclick="toggleNews(this)">
|
||
<div class="news-header">
|
||
<span class="imp-dot breaking"></span>
|
||
<div class="news-title">Meta单周飙升15%创两年最佳表现,AI成本优化报告提振信心</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>AI投资见效,Meta证明烧钱可以有回报,但高估值仍需警惕,等待财报验证</div>
|
||
<div class="news-body-collapse">Meta股价周五大涨6%,全周累计涨15%,为2024年初以来最佳单周表现。路透社披露的内部备忘录显示,Meta可能正在优化AI成本结构,美银维持买入评级。Meta在AI算力投资上显示出比微软、亚马逊更高效的ROI,这缓解了市场对科技巨头无节制烧钱的担忧。二季度财报季即将开始(7月13日大银行率先披露),市场预期标普500成分股盈利增长23.3%,科技板块是关键驱动力。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge breaking">🔥 BREAKING</span>
|
||
<div class="tags"><span class="tag">#科技股</span><span class="tag">#AI</span><span class="tag">#财报季</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="news-item breaking featured" onclick="toggleNews(this)">
|
||
<div class="news-header">
|
||
<span class="imp-dot breaking"></span>
|
||
<div class="news-title">美联储9月加息概率升至58%,油价通胀令降息预期彻底破灭</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>加息预期升温,持有科技股和成长股的投资者需降低仓位或对冲,防御板块和短久期债券更安全</div>
|
||
<div class="news-body-collapse">6月FOMC会议纪要显示委员会在未来路径上存在分歧,部分官员支持若通胀降温则降息,但更多人倾向加息。5月CPI同比涨4.2%,为2023年4月来最高,能源价格飙升是主因。伊朗霍尔木兹海峡冲突虽有停火,但WTI原油仍在70美元上方徘徊。CME FedWatch显示7月28-29日会议维持3.5-3.75%利率概率最高,但9月加息25bp概率47.1%、50bp概率11.1%。这对估值偏高的科技股构成压力。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge breaking">🔥 BREAKING</span>
|
||
<div class="tags"><span class="tag">#美联储</span><span class="tag">#加息</span><span class="tag">#通胀</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="news-item high" onclick="toggleNews(this)">
|
||
<div class="news-header">
|
||
<span class="imp-dot high"></span>
|
||
<div class="news-title">中美农产品关税削减谈判取得原则性进展,但强制劳动调查威胁新关税</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>中美贸易关系仍脆弱,持有中概股和出口型A股需谨慎,短期波动难免,长线布局等确定性</div>
|
||
<div class="news-body-collapse">中国商务部7月2日确认,中美已原则同意将农产品纳入互惠降税框架。5月特朗普-习近平北京会晤后,双方同意各削减300亿美元商品关税,美国向中国提供200架波音飞机及充足发动机供应。但美国贸易代表办公室6月2日提议对华加征12.5%新关税,理由是强制劳动调查,7月7-9日举行听证会。市场预期7月31日前达成全面协议概率仅42.5%,核心分歧仍在稀土出口管制等技术问题。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge high">• HIGH</span>
|
||
<div class="tags"><span class="tag">#中美贸易</span><span class="tag">#关税</span><span class="tag">#政策</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="news-item high" onclick="toggleNews(this)">
|
||
<div class="news-header">
|
||
<span class="imp-dot high"></span>
|
||
<div class="news-title">特斯拉迈阿密推出Robotaxi服务,Q2交付量超预期25%但股价仍承压</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>Robotaxi进展提振情绪,但交付量增长可能因油价回落而放缓,估值过高,短线博弈为主</div>
|
||
<div class="news-body-collapse">特斯拉7月3日在迈阿密正式推出无人驾驶Robotaxi服务,这是继德州和加州后第三个州,但服务区域仍限于小范围地理围栏。公司副总裁Lars Moravy预告7月7日将公布德州工厂扩产'重大消息',市场猜测是为Cybercab大规模生产做准备。特斯拉Q2交付48万辆,同比增25%,远超华尔街预期的40.6万辆,但股价年初至今仍跌12%(标普500涨9%)。7月22日财报发布,估值仍是最大争议点(P/E远高于纳指100)。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge high">• HIGH</span>
|
||
<div class="tags"><span class="tag">#特斯拉</span><span class="tag">#自动驾驶</span><span class="tag">#电动车</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="news-item high" onclick="toggleNews(this)">
|
||
<div class="news-header">
|
||
<span class="imp-dot high"></span>
|
||
<div class="news-title">Nvidia、应用材料引领芯片股反弹,但半导体板块波动加剧</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>芯片股短期波动加剧,长期AI需求仍在,逢低分批布局优质标的,避免追高</div>
|
||
<div class="news-body-collapse">周四芯片股强劲反弹,VanEck半导体ETF(SMH)涨2.5%,Micron涨4.5%、SanDisk涨7.6%。Nvidia周五续涨4%,应用材料(AMAT)管理层对芯片设备需求发表积极评论提振板块。但周五早盘芯片股回吐部分涨幅,纳指一度承压。市场担忧:①SpaceX加入纳斯达克100分流资金②SK Hynix上市加剧竞争③油价回落可能抑制AI基建投资。下周ASML和台积电财报(7月中旬)将是关键风向标。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge high">• HIGH</span>
|
||
<div class="tags"><span class="tag">#芯片</span><span class="tag">#Nvidia</span><span class="tag">#波动</span></div>
|
||
</div>
|
||
</div>
|
||
<!-- NEWS:END -->
|
||
<!-- NEWS:END -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI 操盘实验 -->
|
||
<div class="section-block sec-anchor" id="sec-ai-trading">
|
||
<div class="card">
|
||
<div class="section-label">🤖 AI 操盘实验 · AI Trading Challenge</div>
|
||
<div id="ai-portfolio-loading" style="color:var(--muted);font-size:0.82rem;padding:8px 0;">加载中…</div>
|
||
<div id="ai-portfolio-content" style="display:none">
|
||
<!-- 排行榜 -->
|
||
<div style="font-size:0.72rem;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);margin-bottom:10px;">🏆 排行榜 · Leaderboard <span style="font-size:0.68rem;color:var(--dimmed);margin-left:6px;">初始资金 $10,000 · 基线 SPY买入持有</span></div>
|
||
<div id="ai-leaderboard" style="display:flex;flex-direction:column;gap:8px;margin-bottom:18px;"></div>
|
||
<!-- 各模型详情 Tab -->
|
||
<div style="display:flex;gap:6px;margin-bottom:12px;" id="ai-model-tabs"></div>
|
||
<div id="ai-model-detail"></div>
|
||
<!-- 历史对比表 -->
|
||
<div id="ai-history-wrap" style="margin-top:16px;">
|
||
<div style="font-size:0.72rem;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);margin-bottom:8px;">📈 历史表现对比</div>
|
||
<div id="ai-history-table" style="font-family:var(--mono);font-size:0.76rem;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI WATCHLIST -->
|
||
<section class="section-block sec-anchor" id="sec-watchlist">
|
||
<div class="card">
|
||
<div class="section-label">🎯 AI 决策仪表盘 · AI Watchlist</div>
|
||
<!-- WATCHLIST_ANALYSIS:START -->
|
||
<div class="watchlist-grid">
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">NVDA</div><div class="wc-name">英伟达</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">202.85</div><div class="wc-price-chg down">-0.6%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>72</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">短期震荡,长期AI需求强劲</div>
|
||
<div class="wc-driver">SK Hynix上市分流资金,但数据中心AI需求持续爆发,P/E降至31.7x,运营利润率64%创纪录</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 225</span><span class="wc-target-down">↓ 185</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge medium">风险:medium</span></div>
|
||
<div class="wc-action">等待Q2财报(7月下旬预期),短期波动加大,分批建仓,避免追高,支撑位200美元附近</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#AI</span><span class="tag">#芯片</span><span class="tag">#数据中心</span></div>
|
||
</div>
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">QQQ</div><div class="wc-name">纳斯达克100 ETF</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">估算约540</div><div class="wc-price-chg up">+0.3%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>68</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">科技股估值偏高,需等待回调</div>
|
||
<div class="wc-driver">Meta、Nvidia领涨,但加息预期和高估值是双重压力,Q2盈利增23.3%能否兑现是关键</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 570</span><span class="wc-target-down">↓ 510</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge medium">风险:medium</span></div>
|
||
<div class="wc-action">持有者可部分止盈,新资金等530以下分批买入,关注7月FOMC会议和大型科技股财报</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#科技</span><span class="tag">#ETF</span><span class="tag">#纳指</span></div>
|
||
</div>
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">SPY</div><div class="wc-name">标普500 ETF</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">估算约757</div><div class="wc-price-chg up">+0.4%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>70</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">大盘窄幅震荡,等待方向</div>
|
||
<div class="wc-driver">财报季开启,能源和科技盈利强劲,但加息预期和巴菲特指标历史高位令估值承压</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 7800</span><span class="wc-target-down">↓ 7300</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge low">风险:low</span></div>
|
||
<div class="wc-action">均衡配置者继续持有,短期波动加大,可增加防御板块(医疗、必需消费品)对冲</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#大盘</span><span class="tag">#ETF</span><span class="tag">#多元化</span></div>
|
||
</div>
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">BTC-USD</div><div class="wc-name">比特币</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">63,848</div><div class="wc-price-chg up">+1.08%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge SELL">SELL</span>
|
||
<span class="wc-score">评分 <span>42</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">技术面显示下行风险高</div>
|
||
<div class="wc-driver">Elliott波浪模型显示2022-2026涨势可能是完整五浪,5月高点82,814后回落,目标49,217(对角线起点),季度分析师预期'长期顶部已形成'</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 70000</span><span class="wc-target-down">↓ 49000</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge high">风险:high</span></div>
|
||
<div class="wc-action">高位持有者建议减仓止盈,新资金观望,等待49k-50k区域再考虑配置,风险偏好者可小仓位博弈</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#加密货币</span><span class="tag">#高风险</span><span class="tag">#技术分析</span></div>
|
||
</div>
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">510300.SS</div><div class="wc-name">沪深300 ETF</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">估算约对应CSI 300</div><div class="wc-price-chg down">-1.96%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>55</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">技术面偏弱,政策是关键</div>
|
||
<div class="wc-driver">上证跌破4040支撑位,技术面'预示进一步下跌',科技和工业板块拖累,但中美关税谈判进展提供支撑</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 4200</span><span class="wc-target-down">↓ 3800</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge medium">风险:medium</span></div>
|
||
<div class="wc-action">长线配置者可在3900-4000区域分批建仓,短期避免追涨,等待稳增长政策明朗</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#A股</span><span class="tag">#中国</span><span class="tag">#政策驱动</span></div>
|
||
</div>
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">GC=F</div><div class="wc-name">黄金期货</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">4,128.90</div><div class="wc-price-chg up">+0.37%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge BUY">BUY</span>
|
||
<span class="wc-score">评分 <span>75</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">避险需求上升,中期看涨</div>
|
||
<div class="wc-driver">从1月5,598高点回落26%至6月低点3,942,但达利欧、戴蒙看好长期,央行储备管理压力、土耳其等国抵押黄金换美元压低短期价格,State Street目标5000美元(2027年初)</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 4500</span><span class="wc-target-down">↓ 3900</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge low">风险:low</span></div>
|
||
<div class="wc-action">4100-4200区域是良好买点,配置5-10%对冲通胀和地缘风险,长线持有,目标4500+</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#黄金</span><span class="tag">#避险</span><span class="tag">#通胀对冲</span></div>
|
||
</div>
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">CL=F</div><div class="wc-name">WTI原油期货</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">71.46</div><div class="wc-price-chg down">-0.86%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>60</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">停火缓解供应担忧,短期承压</div>
|
||
<div class="wc-driver">伊朗霍尔木兹海峡停火后从峰值回落,但仍比冲突前高20美元,年底期货仍暗示80美元,供应链中断影响持续,Q3能源板块盈利增速或放缓</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 80</span><span class="wc-target-down">↓ 65</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge medium">风险:medium</span></div>
|
||
<div class="wc-action">能源股持有者可部分止盈,油价在70-75区间震荡,突破80做多,跌破67做空信号</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#原油</span><span class="tag">#能源</span><span class="tag">#地缘政治</span></div>
|
||
</div>
|
||
<div class="watchlist-card">
|
||
<div class="wc-header">
|
||
<div><div class="wc-ticker">TSLA</div><div class="wc-name">特斯拉</div></div>
|
||
<div class="wc-price"><div class="wc-price-val">406.11</div><div class="wc-price-chg up">+3.1%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge SELL">SELL</span>
|
||
<span class="wc-score">评分 <span>48</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">交付量强劲但估值过高</div>
|
||
<div class="wc-driver">Q2交付48万辆超预期25%,迈阿密Robotaxi上线,德州工厂扩产,但年初至今跌12%(大盘涨9%),P/E远超纳指100,油价回落可能削弱EV需求,7月22日财报是关键</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 500</span><span class="wc-target-down">↓ 360</span></div>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px"><span class="risk-badge high">风险:high</span></div>
|
||
<div class="wc-action">短线交易者可博弈财报前冲高,但长线投资者建议减仓,估值不支持当前价格,等360附近再考虑</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#电动车</span><span class="tag">#自动驾驶</span><span class="tag">#高估值</span></div>
|
||
</div>
|
||
</div>
|
||
<!-- WATCHLIST_ANALYSIS:END -->
|
||
<!-- WATCHLIST_ANALYSIS:END -->
|
||
</div>
|
||
</section>
|
||
|
||
<!-- MACRO + DEBATE (2-up) -->
|
||
<div class="two-up">
|
||
<div class="section-block sec-anchor" id="sec-macro">
|
||
<div class="card" style="height:100%">
|
||
<div class="section-label">📈 宏观指标 · Macro</div>
|
||
<!-- MACRO:START -->
|
||
<div class="macro-grid">
|
||
<div class="macro-card dir-up">
|
||
<div class="macro-indicator">CPI YoY</div>
|
||
<div class="macro-value macro-value-down">4.2%</div>
|
||
<div class="macro-prev"><span class="arrow up">↑</span> 前值: 3.8%</div>
|
||
<div class="macro-desc">5月CPI同比涨4.2%,为2023年4月来最高,能源价格飙升是主因,超预期上涨令降息预期破灭</div>
|
||
</div>
|
||
<div class="macro-card dir-neutral">
|
||
<div class="macro-indicator">Core CPI YoY</div>
|
||
<div class="macro-value ">2.6%</div>
|
||
<div class="macro-prev"><span class="arrow ">→</span> 前值: 2.6%</div>
|
||
<div class="macro-desc">核心CPI同比持平2.6%,略好于去年但仍高于Fed 2%目标,服务业通胀粘性显现</div>
|
||
</div>
|
||
<div class="macro-card dir-up">
|
||
<div class="macro-indicator">PCE Price Index YoY</div>
|
||
<div class="macro-value macro-value-down">4.1%</div>
|
||
<div class="macro-prev"><span class="arrow up">↑</span> 前值: 3.0%</div>
|
||
<div class="macro-desc">5月PCE物价指数同比涨4.1%,Fed偏好的通胀指标大幅上升,推动加息预期</div>
|
||
</div>
|
||
<div class="macro-card dir-up">
|
||
<div class="macro-indicator">Core PCE YoY (SEP预测2026)</div>
|
||
<div class="macro-value macro-value-down">3.3%</div>
|
||
<div class="macro-prev"><span class="arrow up">↑</span> 前值: 2.7%</div>
|
||
<div class="macro-desc">6月FOMC将2026年核心PCE预测从2.7%上调至3.3%,显著调高0.6个百分点</div>
|
||
</div>
|
||
<div class="macro-card dir-down">
|
||
<div class="macro-indicator">Nonfarm Payrolls</div>
|
||
<div class="macro-value macro-value-down">57,000</div>
|
||
<div class="macro-prev"><span class="arrow down">↓</span> 前值: N/A</div>
|
||
<div class="macro-desc">6月新增非农就业5.7万,低于预期,但失业率降至4.2%,劳动力市场降温但未崩溃</div>
|
||
</div>
|
||
<div class="macro-card dir-down">
|
||
<div class="macro-indicator">Unemployment Rate</div>
|
||
<div class="macro-value macro-value-up">4.2%</div>
|
||
<div class="macro-prev"><span class="arrow down">↓</span> 前值: 4.3%</div>
|
||
<div class="macro-desc">失业率从4.3%降至4.2%,表面改善但就业增长放缓,Fed双重目标面临两难</div>
|
||
</div>
|
||
<div class="macro-card dir-neutral">
|
||
<div class="macro-indicator">GDP Growth Q1 2026</div>
|
||
<div class="macro-value ">2.1%</div>
|
||
<div class="macro-prev"><span class="arrow ">→</span> 前值: N/A</div>
|
||
<div class="macro-desc">Q1实际GDP年化增长2.1%(第三次修正值),经济活动保持扩张但不同部门分化</div>
|
||
</div>
|
||
<div class="macro-card dir-down">
|
||
<div class="macro-indicator">ISM Services PMI</div>
|
||
<div class="macro-value macro-value-down">54.0</div>
|
||
<div class="macro-prev"><span class="arrow down">↓</span> 前值: 54.5</div>
|
||
<div class="macro-desc">6月ISM服务业PMI为54.0,略低于5月但仍稳健扩张,库存大跌11.3点至51.2值得关注</div>
|
||
</div>
|
||
</div>
|
||
<!-- MACRO:END -->
|
||
<!-- MACRO:END -->
|
||
</div>
|
||
</div>
|
||
<div class="section-block sec-anchor" id="sec-debate">
|
||
<div class="card" style="height:100%">
|
||
<div class="section-label">⚔️ 多空辩论 · Bull vs Bear</div>
|
||
<!-- MARKET_DEBATE:START -->
|
||
<div class="debate-container">
|
||
<div class="debate-header">标普500 本周展望</div>
|
||
<div class="debate-sides">
|
||
<div class="debate-side bull">
|
||
<div class="debate-side-title">🐂 多方看涨</div>
|
||
<div class="debate-point"><span class="conf-badge high">high</span>Q2财报季开启,盈利增23.3%预期强劲</div>
|
||
<div class="debate-point"><span class="conf-badge high">high</span>Meta、Nvidia等科技巨头AI投资开始见效</div>
|
||
<div class="debate-point"><span class="conf-badge medium">medium</span>油价回落缓解通胀压力,Fed加息概率或下降</div>
|
||
</div>
|
||
<div class="debate-side bear">
|
||
<div class="debate-side-title">🐻 空方看跌</div>
|
||
<div class="debate-point"><span class="conf-badge high">high</span>Fed 9月加息概率升至58%,高利率压制估值</div>
|
||
<div class="debate-point"><span class="conf-badge high">high</span>巴菲特指标历史新高,市场估值过度拉伸</div>
|
||
<div class="debate-point"><span class="conf-badge medium">medium</span>中美贸易不确定性,新关税威胁仍在</div>
|
||
</div>
|
||
</div>
|
||
<div class="debate-verdict">
|
||
<div class="verdict-label">裁判结论 <span class="verdict-lean neutral">NEUTRAL</span></div>
|
||
<div class="verdict-text">短期(本周)中性偏多,财报季开局和油价回落提供支撑,但中期(未来1-3个月)偏谨慎,加息预期、高估值和地缘风险是三重压力。建议:持有优质标的,增加防御配置,保留现金等待回调买入机会。</div>
|
||
</div>
|
||
</div>
|
||
<!-- MARKET_DEBATE:END -->
|
||
<!-- MARKET_DEBATE:END -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- EARNINGS + SECTORS (2-up) -->
|
||
<div class="two-up">
|
||
<div class="section-block sec-anchor" id="sec-earnings">
|
||
<div class="card">
|
||
<div class="section-label">💼 财报追踪 · Earnings</div>
|
||
<!-- EARNINGS:START -->
|
||
<div class="earnings-grid">
|
||
<div class="earnings-card pending">
|
||
<div class="earnings-ticker">TSLA</div>
|
||
<div class="earnings-company">Tesla</div>
|
||
<span class="earnings-beat-badge pending">UPCOMING</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$N/A</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">REPORT DATE</div>
|
||
<div class="eps-value est" style="font-size:0.75rem;">2026-07-22</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>Q2交付48万辆,同比+25%</span></div>
|
||
<div class="earnings-highlight">交付量大超预期,但估值过高仍是争议,迈阿密Robotaxi上线,德州工厂扩产在即</div>
|
||
</div>
|
||
<div class="earnings-card pending">
|
||
<div class="earnings-ticker">PEP</div>
|
||
<div class="earnings-company">PepsiCo</div>
|
||
<span class="earnings-beat-badge pending">UPCOMING</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$N/A</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">REPORT DATE</div>
|
||
<div class="eps-value est" style="font-size:0.75rem;">2026-07-初</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>N/A</span></div>
|
||
<div class="earnings-highlight">花旗下调至中性评级,目标价从170美元降至145美元,担心北美销售疲软和通胀持续</div>
|
||
</div>
|
||
<div class="earnings-card pending">
|
||
<div class="earnings-ticker">DAL</div>
|
||
<div class="earnings-company">Delta Air Lines</div>
|
||
<span class="earnings-beat-badge pending">UPCOMING</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$N/A</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">REPORT DATE</div>
|
||
<div class="eps-value est" style="font-size:0.75rem;">2026-07-初</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>N/A</span></div>
|
||
<div class="earnings-highlight">本周已公布财报,市场关注油价波动对航空业利润率影响</div>
|
||
</div>
|
||
<div class="earnings-card beat">
|
||
<div class="earnings-ticker">AAPL</div>
|
||
<div class="earnings-company">Apple</div>
|
||
<span class="earnings-beat-badge beat">BEAT ✓</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS ACTUAL</div>
|
||
<div class="eps-value actual beat">$已公布</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$已公布</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>$111.2B,同比+17%</span></div>
|
||
<div class="earnings-highlight">Q2财年营收1112亿美元增17%,EPS涨22%,iPhone收入570亿(+22%),大中华区205亿(+28%),不烧钱搞AI却增长加速,服务业务高利润率贡献显著</div>
|
||
</div>
|
||
<div class="earnings-card beat">
|
||
<div class="earnings-ticker">MSFT</div>
|
||
<div class="earnings-company">Microsoft</div>
|
||
<span class="earnings-beat-badge beat">BEAT ✓</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS ACTUAL</div>
|
||
<div class="eps-value actual beat">$已公布</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$已公布</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>Q3预期从701亿降至685亿</span></div>
|
||
<div class="earnings-highlight">Azure AI服务强劲,FY2025预期108亿美元,但资本开支飙升至FY2025的636亿、FY2026的719亿(较FY2019增5倍),市场担忧ROI</div>
|
||
</div>
|
||
<div class="earnings-card beat">
|
||
<div class="earnings-ticker">META</div>
|
||
<div class="earnings-company">Meta Platforms</div>
|
||
<span class="earnings-beat-badge beat">BEAT ✓</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS ACTUAL</div>
|
||
<div class="eps-value actual beat">$已公布</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$已公布</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>Q1强劲</span></div>
|
||
<div class="earnings-highlight">单周涨15%创2024年初来最佳,AI成本优化内部备忘录提振信心,美银维持买入,显示AI投资见效</div>
|
||
</div>
|
||
<div class="earnings-card beat">
|
||
<div class="earnings-ticker">AMZN</div>
|
||
<div class="earnings-company">Amazon</div>
|
||
<span class="earnings-beat-badge beat">BEAT ✓</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS ACTUAL</div>
|
||
<div class="eps-value actual beat">$已公布</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$已公布</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>2025年全年7169亿美元</span></div>
|
||
<div class="earnings-highlight">2025年营收增12.4%,可能首次超越沃尔玛成全球最大公司,AWS云和AI持续投入,资本开支大幅上升</div>
|
||
</div>
|
||
<div class="earnings-card pending">
|
||
<div class="earnings-ticker">JPM等</div>
|
||
<div class="earnings-company">大型银行(JPM等)</div>
|
||
<span class="earnings-beat-badge pending">UPCOMING</span>
|
||
<div class="earnings-eps">
|
||
<div class="eps-item">
|
||
<div class="eps-label">EPS EST.</div>
|
||
<div class="eps-value est">$N/A</div>
|
||
</div>
|
||
<div class="eps-item">
|
||
<div class="eps-label">REPORT DATE</div>
|
||
<div class="eps-value est" style="font-size:0.75rem;">2026-07-13起</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>N/A</span></div>
|
||
<div class="earnings-highlight">7月13日当周大银行开启Q2财报季,标普500预期Q2盈利增23.3%,能源和科技领涨</div>
|
||
</div>
|
||
</div>
|
||
<!-- EARNINGS:END -->
|
||
<!-- EARNINGS:END -->
|
||
</div>
|
||
</div>
|
||
<div class="section-block sec-anchor" id="sec-sectors">
|
||
<div class="card">
|
||
<div class="section-label">🏭 板块表现 · Sectors</div>
|
||
<!-- SECTORS:START -->
|
||
<div class="sectors-list">
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">科技</div><div class="sector-name-en">Technology</div></div>
|
||
<div class="sector-etf">XLK</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:100%"></div></div>
|
||
<div class="sector-pct up">+4.9%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">能源</div><div class="sector-name-en">Energy</div></div>
|
||
<div class="sector-etf">XLE</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar down" style="width:30%"></div></div>
|
||
<div class="sector-pct down">-1.1%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">金融</div><div class="sector-name-en">Financials</div></div>
|
||
<div class="sector-etf">XLF</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:74%"></div></div>
|
||
<div class="sector-pct up">+3.5%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">工业</div><div class="sector-name-en">Industrials</div></div>
|
||
<div class="sector-etf">XLI</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:52%"></div></div>
|
||
<div class="sector-pct up">+2.3%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">医疗保健</div><div class="sector-name-en">Healthcare</div></div>
|
||
<div class="sector-etf">XLV</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar down" style="width:35%"></div></div>
|
||
<div class="sector-pct down">-1.41%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">非必需消费品</div><div class="sector-name-en">Consumer Discretionary</div></div>
|
||
<div class="sector-etf">XLY</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:50%"></div></div>
|
||
<div class="sector-pct up">+2.2%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">必需消费品</div><div class="sector-name-en">Consumer Staples</div></div>
|
||
<div class="sector-etf">XLP</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:25%"></div></div>
|
||
<div class="sector-pct up">+0.84%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">通讯服务</div><div class="sector-name-en">Communication Services</div></div>
|
||
<div class="sector-etf">XLC</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:74%"></div></div>
|
||
<div class="sector-pct up">+3.5%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">材料</div><div class="sector-name-en">Materials</div></div>
|
||
<div class="sector-etf">XLB</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:24%"></div></div>
|
||
<div class="sector-pct up">+0.81%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">房地产</div><div class="sector-name-en">Real Estate</div></div>
|
||
<div class="sector-etf">XLRE</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar down" style="width:11%"></div></div>
|
||
<div class="sector-pct neutral">-0.06%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">公用事业</div><div class="sector-name-en">Utilities</div></div>
|
||
<div class="sector-etf">XLU</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:19%"></div></div>
|
||
<div class="sector-pct up">+0.5%</div>
|
||
</div>
|
||
</div>
|
||
<!-- SECTORS:END -->
|
||
<!-- SECTORS:END -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- REPLAY + CALENDAR (2-up) -->
|
||
<div class="two-up">
|
||
<div class="section-block sec-anchor" id="sec-replay">
|
||
<div class="card">
|
||
<div class="section-label">📽️ 大盘复盘 · Replay</div>
|
||
<!-- MARKET_REPLAY:START -->
|
||
<div class="replay-grid"><div class="replay-card">
|
||
<div class="replay-card-title">🇺🇸 美股</div>
|
||
<div class="replay-summary">美股窄幅收涨,科技股领涨,SK Hynix上市提振芯片板块</div>
|
||
<div class="ad-bar-wrap">
|
||
<span class="ad-label-up">↑估算约3200</span>
|
||
<div class="ad-bar adv" style="width:50px;max-width:80px"></div>
|
||
<div class="ad-bar dec" style="width:50px;max-width:80px"></div>
|
||
<span class="ad-label-dn">↓估算约1900</span>
|
||
</div>
|
||
<div class="replay-stats">
|
||
<div class="replay-stat"><div class="replay-stat-label">热门板块</div><div class="replay-stat-val">科技(Meta+6%周涨15%)</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">领涨标的</div><div class="replay-stat-val">SK Hynix(SKHYV)上市首日+13%</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">创52周新高</div><div class="replay-stat-val">未获取具体数据</div></div>
|
||
</div>
|
||
</div><div class="replay-card">
|
||
<div class="replay-card-title">🇨🇳 A股</div>
|
||
<div class="replay-summary">A股承压下跌,上证失守4000点,等待政策发力</div>
|
||
<div class="ad-bar-wrap">
|
||
<span class="ad-label-up">↑估算约1200</span>
|
||
<div class="ad-bar adv" style="width:50px;max-width:80px"></div>
|
||
<div class="ad-bar dec" style="width:50px;max-width:80px"></div>
|
||
<span class="ad-label-dn">↓估算约2800</span>
|
||
</div>
|
||
<div class="replay-stats">
|
||
<div class="replay-stat"><div class="replay-stat-label">热门板块</div><div class="replay-stat-val">无明显强势板块</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">领涨标的</div><div class="replay-stat-val">板块普跌,科技工业领跌</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">北向资金</div><div class="replay-stat-val">估算净流出</div></div>
|
||
</div>
|
||
</div></div>
|
||
<!-- MARKET_REPLAY:END -->
|
||
<!-- MARKET_REPLAY:END -->
|
||
</div>
|
||
</div>
|
||
<div class="section-block sec-anchor" id="sec-calendar">
|
||
<div class="card">
|
||
<div class="section-label">📅 本周日程 · Calendar</div>
|
||
<!-- CALENDAR:START -->
|
||
<div class="calendar-grid">
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周二 7/15<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM ET</span></div>
|
||
<div>
|
||
<div class="cal-event">6月零售销售</div>
|
||
<div class="cal-values">预期: <span>预期放缓</span> · 前值: N/A</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周三 7/16<br><span style="font-size:0.6rem;color:var(--dimmed)">全天</span></div>
|
||
<div>
|
||
<div class="cal-event">大型银行财报(JPM、C、BAC等)</div>
|
||
<div class="cal-values">预期: <span>Q2盈利增速强劲</span> · 前值: N/A</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周四 7/17<br><span style="font-size:0.6rem;color:var(--dimmed)">全天</span></div>
|
||
<div>
|
||
<div class="cal-event">ASML、台积电财报</div>
|
||
<div class="cal-values">预期: <span>芯片设备需求关键指标</span> · 前值: N/A</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周二 7/22<br><span style="font-size:0.6rem;color:var(--dimmed)">盘后</span></div>
|
||
<div>
|
||
<div class="cal-event">特斯拉Q2财报</div>
|
||
<div class="cal-values">预期: <span>营收增长强劲但估值争议</span> · 前值: Q2交付48万辆超预期</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周一-周二 7/28-29<br><span style="font-size:0.6rem;color:var(--dimmed)">2:00 PM ET(周二)</span></div>
|
||
<div>
|
||
<div class="cal-event">FOMC利率决议</div>
|
||
<div class="cal-values">预期: <span>预期维持不变,但声明措辞或转鹰</span> · 前值: 维持3.50-3.75%</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周四 7/30<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM ET</span></div>
|
||
<div>
|
||
<div class="cal-event">Q2 GDP初值</div>
|
||
<div class="cal-values">预期: <span>预期增长放缓</span> · 前值: Q1增2.1%</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周五 8/7<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM ET</span></div>
|
||
<div>
|
||
<div class="cal-event">7月非农就业报告</div>
|
||
<div class="cal-values">预期: <span>劳动力市场持续降温</span> · 前值: 6月新增5.7万</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周三 8/12<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM ET</span></div>
|
||
<div>
|
||
<div class="cal-event">7月CPI报告</div>
|
||
<div class="cal-values">预期: <span>油价回落可能缓解压力</span> · 前值: 5月同比4.2%</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- CALENDAR:END -->
|
||
<!-- CALENDAR:END -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- end main-col -->
|
||
|
||
<!-- ── RIGHT / SIDEBAR ────────────────────────────────── -->
|
||
<div class="sidebar-col">
|
||
|
||
<!-- LEADERS -->
|
||
<div class="section-block sec-anchor mb16" id="sec-leaders">
|
||
<div class="card">
|
||
<div class="section-label">👔 领袖观点 · Leaders</div>
|
||
<!-- LEADERS:START -->
|
||
<div class="leader-card">
|
||
<div class="leader-header">
|
||
<div class="leader-avatar jd">JD</div>
|
||
<div class="leader-meta">
|
||
<div class="leader-name">杰米·戴蒙 <span style="color:var(--muted);font-weight:400;font-size:0.78rem;">Jamie Dimon</span></div>
|
||
<div class="leader-role">摩根大通CEO</div>
|
||
</div>
|
||
<div class="leader-date">4月</div>
|
||
</div>
|
||
<div class="leader-quote">"利率就像万有引力,对几乎所有资产价格都有影响"</div>
|
||
<div class="leader-body">戴蒙在2026年致股东信中警告,通胀上升(无论是否伴随衰退)是最大担忧,这将导致利率走高。他呼应巴菲特26年前的观点,强调当利率攀升时,投资者可以通过国债获得更高无风险回报,这意味着股票等风险资产的预期回报率必须上升,唯一途径就是价格下跌。这一警告对当前估值偏高的市场尤为重要。</div>
|
||
<div class="tags"><span class="tag">#利率</span><span class="tag">#通胀</span></div>
|
||
</div>
|
||
<div class="leader-card">
|
||
<div class="leader-header">
|
||
<div class="leader-avatar wb">WB</div>
|
||
<div class="leader-meta">
|
||
<div class="leader-name">沃伦·巴菲特 <span style="color:var(--muted);font-weight:400;font-size:0.78rem;">Warren Buffett</span></div>
|
||
<div class="leader-role">伯克希尔哈撒韦CEO</div>
|
||
</div>
|
||
<div class="leader-date">7月初</div>
|
||
</div>
|
||
<div class="leader-quote">"股市就像教堂旁边附带了一个赌场"</div>
|
||
<div class="leader-body">巴菲特在2026年伯克希尔股东大会接受CNBC采访时警告投资者,许多股票目前估值过高,投机情绪浓厚。他强调长期持有优质公司的重要性,并指出其理想持有期是'永远'。他的巴菲特指标(美国股市总市值/GDP)最近创下历史新高,这曾在互联网泡沫破裂前发出警告。对过度炒作的AI股票,巴菲特建议投资者保持冷静和长期视角。</div>
|
||
<div class="tags"><span class="tag">#价值投资</span><span class="tag">#估值</span></div>
|
||
</div>
|
||
<div class="leader-card">
|
||
<div class="leader-header">
|
||
<div class="leader-avatar rd">RD</div>
|
||
<div class="leader-meta">
|
||
<div class="leader-name">雷·达利欧 <span style="color:var(--muted);font-weight:400;font-size:0.78rem;">Ray Dalio</span></div>
|
||
<div class="leader-role">桥水基金创始人</div>
|
||
</div>
|
||
<div class="leader-date">2025年</div>
|
||
</div>
|
||
<div class="leader-quote">"人们的投资组合中通常没有足够的黄金配置"</div>
|
||
<div class="leader-body">达利欧去年告诉CNBC,当糟糕时期来临时,黄金是非常有效的分散投资工具。尽管金价近期从1月的5,598美元高点回落26%至约4,140美元,但他认为在当前美伊冲突、通胀粘性和央行储备管理压力下,黄金仍具吸引力。摩根大通CEO戴蒙甚至表示金价可能'轻松'涨至1万美元/盎司。达利欧建议投资者增加黄金配置以对冲经济不确定性。</div>
|
||
<div class="tags"><span class="tag">#黄金</span><span class="tag">#避险</span></div>
|
||
</div>
|
||
<div class="leader-card">
|
||
<div class="leader-header">
|
||
<div class="leader-avatar jp">JP</div>
|
||
<div class="leader-meta">
|
||
<div class="leader-name">杰罗姆·鲍威尔 <span style="color:var(--muted);font-weight:400;font-size:0.78rem;">Jerome Powell</span></div>
|
||
<div class="leader-role">前美联储主席</div>
|
||
</div>
|
||
<div class="leader-date">6月17日</div>
|
||
</div>
|
||
<div class="leader-quote">"通胀预期已攀升,2026年中值预测达2.7%"</div>
|
||
<div class="leader-body">鲍威尔在6月FOMC会议上指出,通胀预期今年大幅上升,核心PCE通胀预测从2.4%上调至3.6%。新任主席Kevin Warsh在首次新闻发布会上强调'通胀相对于2%目标仍然偏高',点阵图显示18名官员中有9人预计年内至少加息一次,中值利率预测升至3.8%。市场目前定价显示9月加息25基点概率47.1%,50基点概率11.1%,合计约58%。降息预期已基本消失。</div>
|
||
<div class="tags"><span class="tag">#美联储</span><span class="tag">#通胀</span></div>
|
||
</div>
|
||
<!-- LEADERS:END -->
|
||
<!-- LEADERS:END -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CENTRAL BANKS -->
|
||
<div class="section-block sec-anchor mb16" id="sec-banks">
|
||
<div class="card">
|
||
<div class="section-label">🏦 央行动态 · Central Banks</div>
|
||
<!-- CENTRAL_BANKS:START -->
|
||
<div class="cb-grid">
|
||
<div class="cb-card">
|
||
<div class="cb-header">
|
||
<div>
|
||
<div class="cb-name">美联储</div>
|
||
<div class="cb-name-en">Federal Reserve</div>
|
||
</div>
|
||
<span class="bias-badge hawkish">Hawkish</span>
|
||
</div>
|
||
<div class="cb-rate">3.50-3.75%</div>
|
||
<div class="cb-action">维持利率3.50-3.75%不变(6月会议)</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">2026-07-29</span></div>
|
||
<div class="cb-note">新主席Kevin Warsh首次主持会议,9名官员预计年内加息,中值利率预测升至3.8%,通胀粘性令加息概率升至58%</div>
|
||
</div>
|
||
<div class="cb-card">
|
||
<div class="cb-header">
|
||
<div>
|
||
<div class="cb-name">欧央行</div>
|
||
<div class="cb-name-en">European Central Bank</div>
|
||
</div>
|
||
<span class="bias-badge neutral">Neutral</span>
|
||
</div>
|
||
<div class="cb-rate">未具体提及</div>
|
||
<div class="cb-action">维持利率不变(最近会议)</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">2026-07-下旬</span></div>
|
||
<div class="cb-note">欧洲股市(Stoxx 600)涨0.8%,德国DAX涨0.7%,相对美国鸽派,但通胀压力仍存</div>
|
||
</div>
|
||
<div class="cb-card">
|
||
<div class="cb-header">
|
||
<div>
|
||
<div class="cb-name">中国人民银行</div>
|
||
<div class="cb-name-en">People's Bank of China</div>
|
||
</div>
|
||
<span class="bias-badge dovish">Dovish</span>
|
||
</div>
|
||
<div class="cb-rate">LPR未变</div>
|
||
<div class="cb-action">维持宽松政策</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">未明确</span></div>
|
||
<div class="cb-note">A股CSI 300跌1.96%,科技和工业板块拖累,稀土出口管制暂停一年(中美协议),经济复苏仍需政策支持</div>
|
||
</div>
|
||
<div class="cb-card">
|
||
<div class="cb-header">
|
||
<div>
|
||
<div class="cb-name">日本银行</div>
|
||
<div class="cb-name-en">Bank of Japan</div>
|
||
</div>
|
||
<span class="bias-badge dovish">Dovish</span>
|
||
</div>
|
||
<div class="cb-rate">超低利率</div>
|
||
<div class="cb-action">维持宽松政策</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">2026-07-30至31日</span></div>
|
||
<div class="cb-note">日元兑美元接近160关口,财务省干预风险上升,日经225涨1.2%,但汇率压力持续</div>
|
||
</div>
|
||
</div>
|
||
<!-- CENTRAL_BANKS:END -->
|
||
<!-- CENTRAL_BANKS:END -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TRADINGVIEW CHARTS -->
|
||
<div class="section-block mb16">
|
||
<div class="card" style="padding:14px 16px">
|
||
<div class="section-label">📈 行情图表 · Live Charts</div>
|
||
<div class="tradingview-widget-container">
|
||
<div class="tradingview-widget-container__widget"></div>
|
||
<script type="text/javascript" src="https://s3.tradingview.com/external-embedding/embed-widget-market-overview.js" async>
|
||
{
|
||
"colorTheme": "dark",
|
||
"dateRange": "1D",
|
||
"showChart": true,
|
||
"locale": "zh_CN",
|
||
"isTransparent": true,
|
||
"showSymbolLogo": true,
|
||
"showFloatingTooltip": false,
|
||
"width": "100%",
|
||
"height": "480",
|
||
"tabs": [
|
||
{
|
||
"title": "🇺🇸 美股",
|
||
"symbols": [
|
||
{"s":"FOREXCOM:SPXUSD","d":"S&P 500"},
|
||
{"s":"FOREXCOM:NSXUSD","d":"Nasdaq 100"},
|
||
{"s":"FOREXCOM:DJI","d":"道琼斯"},
|
||
{"s":"CBOE:VIX","d":"VIX 恐慌指数"},
|
||
{"s":"TVC:US10Y","d":"美10年债"}
|
||
],
|
||
"originalTitle": "US"
|
||
},
|
||
{
|
||
"title": "🇨🇳 中国",
|
||
"symbols": [
|
||
{"s":"SSE:000001","d":"上证指数"},
|
||
{"s":"SZSE:399006","d":"创业板指"},
|
||
{"s":"HKEX:HSI","d":"恒生指数"},
|
||
{"s":"FX:USDCNH","d":"离岸人民币"},
|
||
{"s":"HKEX:2318","d":"中国平安"}
|
||
],
|
||
"originalTitle": "China"
|
||
},
|
||
{
|
||
"title": "🛢️ 大宗",
|
||
"symbols": [
|
||
{"s":"TVC:GOLD","d":"黄金"},
|
||
{"s":"NYMEX:CL1!","d":"原油 WTI"},
|
||
{"s":"COMEX:SI1!","d":"白银"},
|
||
{"s":"TVC:DXY","d":"美元指数"}
|
||
],
|
||
"originalTitle": "Commodities"
|
||
},
|
||
{
|
||
"title": "₿ 加密",
|
||
"symbols": [
|
||
{"s":"BINANCE:BTCUSDT","d":"Bitcoin"},
|
||
{"s":"BINANCE:ETHUSDT","d":"Ethereum"},
|
||
{"s":"BINANCE:SOLUSDT","d":"Solana"}
|
||
],
|
||
"originalTitle": "Crypto"
|
||
}
|
||
]
|
||
}
|
||
</script>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- end sidebar-col -->
|
||
|
||
</div><!-- end content-grid -->
|
||
|
||
<!-- MY DASHBOARD (collapsed drawer) -->
|
||
<div class="my-drawer sec-anchor" id="sec-my">
|
||
<button class="my-drawer-toggle" onclick="this.classList.toggle('open');this.nextElementSibling.classList.toggle('open')">
|
||
<span>⭐ 我的工作台 · My Dashboard</span>
|
||
<span class="drawer-icon">▾</span>
|
||
</button>
|
||
<div class="my-drawer-body">
|
||
<div class="card">
|
||
<div class="profile-bar">
|
||
<span class="profile-label">👤 用户:</span>
|
||
<select id="profile-select" onchange="profileSwitch()"></select>
|
||
<button onclick="profileNew()">+ 新建</button>
|
||
<button onclick="profileDelete()">删除</button>
|
||
</div>
|
||
<div class="personal-header">
|
||
<div class="section-label">⭐ 我的工作台 · My Dashboard</div>
|
||
<span class="personal-privacy">🔒 数据存本地 · 不上传服务器</span>
|
||
</div>
|
||
<div class="ptabs">
|
||
<button class="ptab active" id="ptab-wl" onclick="switchTab(event,'wl')">📋 自选股</button>
|
||
<button class="ptab" id="ptab-pf" onclick="switchTab(event,'pf')">💼 持仓盈亏</button>
|
||
<button class="ptab" id="ptab-sim" onclick="switchTab(event,'sim')">📊 模拟器</button>
|
||
</div>
|
||
<div id="panel-wl">
|
||
<div class="add-row">
|
||
<input id="wl-sym" placeholder="代码,如 AAPL / 000001.SS / BTC-USD" />
|
||
<input id="wl-name" placeholder="备注名(可选)" />
|
||
<button onclick="wlAdd()">+ 添加</button>
|
||
</div>
|
||
<div id="wl-empty" class="p-empty">还没有自选股,输入代码开始追踪 →</div>
|
||
<div id="wl-list" class="wl-list"></div>
|
||
</div>
|
||
<div id="panel-pf" style="display:none">
|
||
<div class="add-row">
|
||
<input id="pf-sym" placeholder="代码,如 AAPL / TSLA" />
|
||
<input id="pf-shares" type="number" min="0.0001" step="0.0001" placeholder="数量" />
|
||
<input id="pf-cost" type="number" min="0" step="0.01" placeholder="买入均价 ($)" />
|
||
<button onclick="pfAdd()">+ 添加持仓</button>
|
||
</div>
|
||
<div id="pf-empty" class="p-empty">还没有持仓记录,输入持仓开始计算浮盈浮亏 →</div>
|
||
<div id="pf-summary" class="pf-summary" style="display:none">
|
||
<div class="pf-sum-item">总成本<span id="sum-cost">—</span></div>
|
||
<div class="pf-sum-item">总市值<span id="sum-value">—</span></div>
|
||
<div class="pf-sum-item">总盈亏<span id="sum-pnl">—</span></div>
|
||
<div class="pf-sum-item">盈亏率<span id="sum-pct">—</span></div>
|
||
</div>
|
||
<div id="pf-list" class="pf-list"></div>
|
||
</div>
|
||
<div id="panel-sim" style="display:none">
|
||
<div class="sim-section">
|
||
<div>
|
||
<div class="sim-label">总投资金额</div>
|
||
<div class="sim-capital-row">
|
||
<input type="number" id="sim-capital" min="0" step="100" value="10000" oninput="simUpdate()" />
|
||
<select id="sim-currency" onchange="simUpdate()">
|
||
<option value="USD">USD $</option>
|
||
<option value="CAD">CAD $</option>
|
||
<option value="CNY">CNY ¥</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="sim-label">资产配置</div>
|
||
<div class="sim-add-row">
|
||
<input id="sim-sym" placeholder="代码,如 AAPL" style="width:100px" />
|
||
<input id="sim-alias" placeholder="备注名" style="width:85px" />
|
||
<input type="number" id="sim-pct" placeholder="分配%" min="0" max="100" step="1" style="width:65px" />
|
||
<input type="number" id="sim-entry" placeholder="买入价(选填)" min="0" step="0.01" style="width:110px" />
|
||
<button onclick="simAddAsset()">+ 添加</button>
|
||
<span style="font-size:0.68rem;color:var(--dimmed)">不填则自动记录当前价</span>
|
||
</div>
|
||
<div id="sim-pct-total" class="sim-pct-total" style="margin-top:4px">已分配:0%</div>
|
||
</div>
|
||
<div id="sim-alloc-bar" class="sim-alloc-bar" style="display:none"></div>
|
||
<div id="sim-assets-wrap" style="display:none">
|
||
<table class="sim-assets-table">
|
||
<thead><tr><th>代码</th><th>备注</th><th>分配%</th><th>投入金额</th><th>买入价</th><th>操作</th></tr></thead>
|
||
<tbody id="sim-assets-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div id="sim-scenario-section" style="display:none">
|
||
<div class="sim-label">收益情景(年化%)</div>
|
||
<div class="sim-scenario-row">
|
||
<label>😨 悲观 <input type="number" id="sim-bear" value="-20" step="1" oninput="simUpdate()" /></label>
|
||
<label>📊 中性 <input type="number" id="sim-base" value="8" step="1" oninput="simUpdate()" /></label>
|
||
<label>🚀 乐观 <input type="number" id="sim-bull" value="25" step="1" oninput="simUpdate()" /></label>
|
||
</div>
|
||
<div class="sim-horizon-row">
|
||
<span>时间跨度:</span>
|
||
<button class="sim-hbtn" data-h="1m" onclick="simSetHorizon(this,'1m')">1月</button>
|
||
<button class="sim-hbtn" data-h="3m" onclick="simSetHorizon(this,'3m')">3月</button>
|
||
<button class="sim-hbtn" data-h="6m" onclick="simSetHorizon(this,'6m')">6月</button>
|
||
<button class="sim-hbtn active" data-h="1y" onclick="simSetHorizon(this,'1y')">1年</button>
|
||
<button class="sim-hbtn" data-h="3y" onclick="simSetHorizon(this,'3y')">3年</button>
|
||
<button class="sim-hbtn" data-h="5y" onclick="simSetHorizon(this,'5y')">5年</button>
|
||
</div>
|
||
</div>
|
||
<div id="sim-returns-wrap" style="display:none">
|
||
<div class="sim-label">预期收益预测</div>
|
||
<table class="sim-returns-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="text-align:left">资产</th>
|
||
<th>投入</th>
|
||
<th>📈 实际</th>
|
||
<th>😨 悲观</th>
|
||
<th>📊 中性</th>
|
||
<th>🚀 乐观</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="sim-returns-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="sim-actions">
|
||
<button class="sim-btn sim-btn-secondary" onclick="simReset()">🗑 重置</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- end container -->
|
||
</div><!-- end page-body -->
|
||
|
||
<!-- =========================================================
|
||
FOOTER
|
||
========================================================= -->
|
||
<footer class="site-footer">
|
||
<div class="container">
|
||
<div class="footer-inner">
|
||
<span>© 2025 金融日报 · Financial Daily | Powered by <a href="https://www.anthropic.com" target="_blank">Claude AI</a></span>
|
||
<span>内容由AI自动生成,仅供参考,不构成投资建议</span>
|
||
<a href="../feed.xml">RSS Feed</a>
|
||
</div>
|
||
<div style="display:flex;gap:24px;justify-content:center;margin-top:12px;font-size:0.75rem;color:var(--muted);">
|
||
<span>👁 访客 <strong id="footer-visitors" style="color:var(--accent)">…</strong></span>
|
||
<span>📄 浏览 <strong id="footer-pageviews" style="color:var(--accent)">…</strong></span>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
// Set current date
|
||
(function() {
|
||
const el = document.getElementById('current-date');
|
||
if (!el) return;
|
||
const now = new Date();
|
||
const opts = { year: 'numeric', month: 'long', day: 'numeric', weekday: 'short' };
|
||
el.textContent = now.toLocaleDateString('zh-CN', opts);
|
||
})();
|
||
|
||
// Data age indicator
|
||
(function() {
|
||
const metaEl = document.querySelector('meta[name="last-updated"]');
|
||
if (!metaEl) return;
|
||
const updated = new Date(metaEl.content);
|
||
const ageH = (Date.now() - updated) / 3600000;
|
||
const badge = document.querySelector('.header-badge');
|
||
if (!badge) return;
|
||
const ageMin = Math.round(ageH * 60);
|
||
const ageStr = ageH < 1 ? ageMin + '分钟前更新' : Math.round(ageH) + '小时前更新';
|
||
badge.title = ageStr;
|
||
if (ageH > 14) badge.classList.add('data-stale');
|
||
})();
|
||
|
||
// Simple / Advanced mode toggle
|
||
(function() {
|
||
const btn = document.getElementById('mode-btn');
|
||
if (!btn) return;
|
||
const stored = localStorage.getItem('fd-mode') || 'advanced';
|
||
applyMode(stored);
|
||
btn.addEventListener('click', function() {
|
||
const isSimple = document.body.classList.contains('simple-mode');
|
||
const next = isSimple ? 'advanced' : 'simple';
|
||
localStorage.setItem('fd-mode', next);
|
||
applyMode(next);
|
||
});
|
||
function applyMode(mode) {
|
||
const label = btn.querySelector('.mode-label');
|
||
const icon = btn.querySelector('.mode-icon');
|
||
if (mode === 'simple') {
|
||
document.body.classList.add('simple-mode');
|
||
label.textContent = '专业模式';
|
||
icon.textContent = '📊';
|
||
} else {
|
||
document.body.classList.remove('simple-mode');
|
||
label.textContent = '新手模式';
|
||
icon.textContent = '📖';
|
||
}
|
||
}
|
||
})();
|
||
|
||
// Live quotes from data/quotes.json (populated by live-quotes workflow)
|
||
(function() {
|
||
fetch('data/quotes.json?' + Date.now())
|
||
.then(r => r.ok ? r.json() : null)
|
||
.then(data => {
|
||
if (!data || !data.quotes) return;
|
||
const track = document.querySelector('.indices-track');
|
||
if (!track) return;
|
||
data.quotes.forEach(q => {
|
||
track.querySelectorAll('.ticker-card').forEach(card => {
|
||
const nameEl = card.querySelector('.ticker-name');
|
||
if (nameEl && nameEl.textContent.trim() === q.name) {
|
||
card.className = 'ticker-card ' + q.direction;
|
||
card.querySelector('.ticker-value').textContent = q.value;
|
||
const chgEl = card.querySelector('.ticker-change');
|
||
const arrow = q.direction === 'up' ? '▲' : '▼';
|
||
chgEl.textContent = arrow + ' ' + q.change_pct;
|
||
chgEl.className = 'ticker-change ' + q.direction;
|
||
}
|
||
});
|
||
});
|
||
// Show live badge
|
||
const badge = document.querySelector('.header-badge');
|
||
if (badge) {
|
||
const updated = new Date(data.updated);
|
||
const mins = Math.round((Date.now() - updated) / 60000);
|
||
badge.title = '行情数据: ' + (mins < 60 ? mins + '分钟前' : Math.round(mins/60) + '小时前');
|
||
badge.classList.remove('data-stale');
|
||
}
|
||
})
|
||
.catch(() => {});
|
||
})();
|
||
|
||
|
||
|
||
|
||
// Auto-tooltip for financial jargon
|
||
const FIN_TERMS = {
|
||
'VIX': 'VIX = 标普期权隐含波动率,越高越恐慌',
|
||
'PCE': 'PCE = 个人消费支出物价指数,美联储首选通胀指标',
|
||
'NFP': 'NFP = 非农就业人数,衡量美国就业市场健康度',
|
||
'CPI': 'CPI = 消费者物价指数,最常见通胀衡量指标',
|
||
'PMI': 'PMI > 50 = 经济扩张;PMI < 50 = 经济收缩',
|
||
'QE': 'QE = 量化宽松,央行购买债券向市场注入流动性',
|
||
'鹰派': '鹰派 = 倾向加息/收紧货币政策,对抗通胀',
|
||
'鸽派': '鸽派 = 倾向降息/宽松货币政策,刺激经济',
|
||
'EPS': 'EPS = 每股收益,公司盈利能力的核心指标',
|
||
'DXY': 'DXY = 美元指数,衡量美元对六大货币的强弱',
|
||
};
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const walker = document.createTreeWalker(
|
||
document.querySelector('.macro-section') || document.body,
|
||
NodeFilter.SHOW_TEXT
|
||
);
|
||
// Only apply to macro + news sections to avoid performance issues
|
||
['macro-section','news-section','cb-grid'].forEach(cls => {
|
||
const sec = document.querySelector('.' + cls);
|
||
if (!sec) return;
|
||
Object.entries(FIN_TERMS).forEach(([term, tip]) => {
|
||
sec.querySelectorAll('.macro-indicator, .cb-action, .news-body').forEach(el => {
|
||
if (el.innerHTML.includes(term) && !el.innerHTML.includes('fin-term')) {
|
||
el.innerHTML = el.innerHTML.replace(
|
||
new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g,'\\$&') + ')', 'g'),
|
||
`<span class="fin-term" data-tip="${tip}">$1</span>`
|
||
);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
function copyRSS(e) {
|
||
e.preventDefault();
|
||
const url = 'https://yang1bai.github.io/finance-daily-site/feed.xml';
|
||
navigator.clipboard.writeText(url).then(() => {
|
||
const btn = document.getElementById('copy-rss');
|
||
btn.textContent = '✅ 已复制';
|
||
setTimeout(() => { btn.textContent = '📋 复制链接'; }, 2000);
|
||
}).catch(() => {
|
||
prompt('RSS 链接:', url);
|
||
});
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════
|
||
// PERSONAL DASHBOARD — Watchlist + Portfolio
|
||
// ═══════════════════════════════════════════════════════════
|
||
|
||
// ── Multi-user Profiles ──────────────────────────────────────
|
||
function profileLoad() {
|
||
return JSON.parse(localStorage.getItem('fd-profiles-v1') || 'null') || [{ id: 'default', name: '默认用户' }];
|
||
}
|
||
function profileSave(list) { localStorage.setItem('fd-profiles-v1', JSON.stringify(list)); }
|
||
function profileActive() { return localStorage.getItem('fd-active-profile-v1') || 'default'; }
|
||
function profileSetActive(id) { localStorage.setItem('fd-active-profile-v1', id); }
|
||
|
||
function profileRenderSelect() {
|
||
const list = profileLoad();
|
||
const active = profileActive();
|
||
const sel = document.getElementById('profile-select');
|
||
if (!sel) return;
|
||
sel.innerHTML = list.map(p => `<option value="${p.id}" ${p.id === active ? 'selected' : ''}>${p.name}</option>`).join('');
|
||
}
|
||
|
||
function profileSwitch() {
|
||
const sel = document.getElementById('profile-select');
|
||
if (!sel) return;
|
||
profileSetActive(sel.value);
|
||
wlRender(); pfRender(); simRender();
|
||
}
|
||
|
||
function profileNew() {
|
||
const name = prompt('输入用户名:', '用户' + (profileLoad().length + 1));
|
||
if (!name || !name.trim()) return;
|
||
const id = 'user_' + Date.now();
|
||
const list = profileLoad();
|
||
list.push({ id, name: name.trim() });
|
||
profileSave(list);
|
||
profileSetActive(id);
|
||
profileRenderSelect();
|
||
wlRender(); pfRender(); simRender();
|
||
}
|
||
|
||
function profileDelete() {
|
||
const list = profileLoad();
|
||
if (list.length <= 1) { alert('至少保留一个用户'); return; }
|
||
const active = profileActive();
|
||
const idx = list.findIndex(p => p.id === active);
|
||
if (idx === -1) return;
|
||
if (!confirm(`确认删除用户"${list[idx].name}"的所有数据?`)) return;
|
||
['watchlist-v1','portfolio-v1','sim-v1'].forEach(k => {
|
||
localStorage.removeItem(`fd-${active}-${k}`);
|
||
});
|
||
list.splice(idx, 1);
|
||
profileSave(list);
|
||
profileSetActive(list[0].id);
|
||
profileRenderSelect();
|
||
wlRender(); pfRender(); simRender();
|
||
}
|
||
|
||
// ── Dynamic localStorage keys ──────────────────────────
|
||
function lsWL() { return `fd-${profileActive()}-watchlist-v1`; }
|
||
function lsPF() { return `fd-${profileActive()}-portfolio-v1`; }
|
||
function lsSIM() { return `fd-${profileActive()}-sim-v1`; }
|
||
const priceCache = {};
|
||
|
||
function wlLoad() { return JSON.parse(localStorage.getItem(lsWL()) || '[]'); }
|
||
function wlSave(d) { localStorage.setItem(lsWL(), JSON.stringify(d)); }
|
||
function pfLoad() { return JSON.parse(localStorage.getItem(lsPF()) || '[]'); }
|
||
function pfSave(d) { localStorage.setItem(lsPF(), JSON.stringify(d)); }
|
||
|
||
function switchTab(e, tab) {
|
||
['wl','pf','sim'].forEach(t => {
|
||
document.getElementById('panel-' + t).style.display = t === tab ? 'block' : 'none';
|
||
document.getElementById('ptab-' + t).classList.toggle('active', t === tab);
|
||
});
|
||
}
|
||
|
||
// ── Price fetching (Yahoo Finance → CORS proxy fallback) ───
|
||
function toYahooSym(raw) {
|
||
if (!raw.includes(':')) return raw;
|
||
const [exch, ticker] = raw.split(':');
|
||
const map = { SSE:'', SZSE:'', HKEX:'.HK', TSX:'.TO', LSE:'.L', FWB:'.DE' };
|
||
if (exch === 'SSE') return ticker + '.SS';
|
||
if (exch === 'SZSE') return ticker + '.SZ';
|
||
return ticker + (map[exch] || '');
|
||
}
|
||
|
||
async function fetchPrice(rawSym) {
|
||
const key = rawSym;
|
||
if (priceCache[key] && Date.now() - priceCache[key].ts < 300000) return priceCache[key];
|
||
|
||
const ySym = toYahooSym(rawSym);
|
||
const yUrl = `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(ySym)}?range=1d&interval=1m&includePrePost=false`;
|
||
const proxies = [
|
||
yUrl,
|
||
`https://corsproxy.io/?${encodeURIComponent(yUrl)}`,
|
||
`https://api.allorigins.win/get?url=${encodeURIComponent(yUrl)}`
|
||
];
|
||
|
||
for (const url of proxies) {
|
||
try {
|
||
const resp = await fetch(url, { signal: AbortSignal.timeout(7000) });
|
||
if (!resp.ok) continue;
|
||
let json = await resp.json();
|
||
// allorigins wraps in {contents: '...'}
|
||
if (json.contents) json = JSON.parse(json.contents);
|
||
const result = json.chart?.result?.[0];
|
||
if (!result) continue;
|
||
const meta = result.meta;
|
||
const price = meta.regularMarketPrice;
|
||
const prev = meta.chartPreviousClose || meta.previousClose || price;
|
||
const chgPct = prev ? (price - prev) / prev * 100 : 0;
|
||
const chgAbs = price - prev;
|
||
const info = { price, prev, chgPct, chgAbs, currency: meta.currency || 'USD', ts: Date.now() };
|
||
priceCache[key] = info;
|
||
return info;
|
||
} catch(e) {}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function fmtNum(n, dec=2) {
|
||
return n.toLocaleString('en-US', { minimumFractionDigits: dec, maximumFractionDigits: dec });
|
||
}
|
||
|
||
// ── Watchlist ───────────────────────────────────────────────
|
||
function wlAdd() {
|
||
const sym = (document.getElementById('wl-sym').value || '').trim().toUpperCase();
|
||
const name = (document.getElementById('wl-name').value || '').trim();
|
||
if (!sym) return;
|
||
const list = wlLoad();
|
||
if (list.find(i => i.symbol === sym)) { alert('已在自选列表'); return; }
|
||
list.push({ symbol: sym, alias: name });
|
||
wlSave(list);
|
||
document.getElementById('wl-sym').value = '';
|
||
document.getElementById('wl-name').value = '';
|
||
wlRender();
|
||
}
|
||
|
||
function wlRemove(sym) {
|
||
wlSave(wlLoad().filter(i => i.symbol !== sym));
|
||
wlRender();
|
||
}
|
||
|
||
function wlRender() {
|
||
const list = wlLoad();
|
||
const grid = document.getElementById('wl-list');
|
||
const empty = document.getElementById('wl-empty');
|
||
if (!list.length) { empty.style.display = 'block'; grid.innerHTML = ''; return; }
|
||
empty.style.display = 'none';
|
||
grid.innerHTML = list.map(item => {
|
||
const id = 'wlp-' + item.symbol.replace(/[^a-z0-9]/gi, '_');
|
||
return `<div class="wl-card">
|
||
<div class="wl-card-top">
|
||
<div>
|
||
<div class="wl-sym">${item.symbol}</div>
|
||
${item.alias ? `<div class="wl-alias">${item.alias}</div>` : ''}
|
||
</div>
|
||
<button class="wl-rm" onclick="wlRemove('${item.symbol.replace(/'/g,"\'")}')">×</button>
|
||
</div>
|
||
<div id="${id}"><span class="wl-loading">加载中…</span></div>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
list.forEach(item => {
|
||
const id = 'wlp-' + item.symbol.replace(/[^a-z0-9]/gi, '_');
|
||
fetchPrice(item.symbol).then(info => {
|
||
const el = document.getElementById(id);
|
||
if (!el) return;
|
||
if (!info) { el.innerHTML = '<span class="wl-loading">获取失败</span>'; return; }
|
||
const dir = info.chgPct >= 0 ? 'up' : 'down';
|
||
const arrow = info.chgPct >= 0 ? '▲' : '▼';
|
||
el.innerHTML = `
|
||
<div class="wl-price-block">
|
||
<span class="wl-cur">${info.currency}</span>
|
||
<span class="wl-price">${fmtNum(info.price)}</span>
|
||
<span class="ticker-change ${dir}" style="font-size:0.72rem;">${arrow} ${Math.abs(info.chgPct).toFixed(2)}%</span>
|
||
</div>
|
||
<div class="wl-prev">昨收 ${fmtNum(info.prev)}</div>
|
||
`;
|
||
});
|
||
});
|
||
}
|
||
|
||
// ── Portfolio ───────────────────────────────────────────────
|
||
function pfAdd() {
|
||
const sym = (document.getElementById('pf-sym').value || '').trim().toUpperCase();
|
||
const shares = parseFloat(document.getElementById('pf-shares').value);
|
||
const cost = parseFloat(document.getElementById('pf-cost').value);
|
||
if (!sym || !shares || !cost || isNaN(shares) || isNaN(cost)) {
|
||
alert('请完整填写代码、数量和买入均价');
|
||
return;
|
||
}
|
||
const list = pfLoad();
|
||
list.push({ symbol: sym, shares, costBasis: cost, addedAt: Date.now() });
|
||
pfSave(list);
|
||
['pf-sym','pf-shares','pf-cost'].forEach(id => document.getElementById(id).value = '');
|
||
pfRender();
|
||
}
|
||
|
||
function pfRemove(idx) {
|
||
const list = pfLoad(); list.splice(idx, 1); pfSave(list); pfRender();
|
||
}
|
||
|
||
function pfRender() {
|
||
const list = pfLoad();
|
||
const grid = document.getElementById('pf-list');
|
||
const empty = document.getElementById('pf-empty');
|
||
const sumEl = document.getElementById('pf-summary');
|
||
if (!list.length) { empty.style.display='block'; grid.innerHTML=''; sumEl.style.display='none'; return; }
|
||
empty.style.display = 'none';
|
||
sumEl.style.display = 'flex';
|
||
|
||
const totalCost = list.reduce((s, i) => s + i.shares * i.costBasis, 0);
|
||
document.getElementById('sum-cost').textContent = '$' + fmtNum(totalCost);
|
||
|
||
grid.innerHTML = list.map((item, idx) => {
|
||
const id = 'pfp-' + idx;
|
||
const itemCost = item.shares * item.costBasis;
|
||
return `<div class="pf-card">
|
||
<div class="pf-card-top">
|
||
<span class="pf-sym">${item.symbol}</span>
|
||
<button class="wl-rm" onclick="pfRemove(${idx})">×</button>
|
||
</div>
|
||
<div class="pf-meta">
|
||
${fmtNum(item.shares, 4)} 股 × $${fmtNum(item.costBasis)} 成本<br>
|
||
持仓成本 <strong>$${fmtNum(itemCost)}</strong>
|
||
</div>
|
||
<div class="pf-pnl-row" id="${id}"><span class="wl-loading">计算盈亏中…</span></div>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
let fetchedCount = 0, totalValue = 0;
|
||
list.forEach((item, idx) => {
|
||
fetchPrice(item.symbol).then(info => {
|
||
fetchedCount++;
|
||
const el = document.getElementById('pfp-' + idx);
|
||
if (!el) return;
|
||
if (!info) { el.innerHTML = '<span class="wl-loading">价格获取失败</span>'; return; }
|
||
const curVal = item.shares * info.price;
|
||
const pnlAbs = curVal - item.shares * item.costBasis;
|
||
const pnlPct = pnlAbs / (item.shares * item.costBasis) * 100;
|
||
const dir = pnlAbs >= 0 ? 'pos' : 'neg';
|
||
const sign = pnlAbs >= 0 ? '+' : '';
|
||
totalValue += curVal;
|
||
el.innerHTML = `
|
||
<span class="pf-cur-price">${info.currency} ${fmtNum(info.price)}</span>
|
||
<span class="pf-gain ${dir}">${sign}$${fmtNum(pnlAbs)} (${sign}${pnlPct.toFixed(2)}%)</span>
|
||
`;
|
||
if (fetchedCount === list.length) {
|
||
document.getElementById('sum-value').textContent = '$' + fmtNum(totalValue);
|
||
const totPnl = totalValue - totalCost;
|
||
const totPct = totPnl / totalCost * 100;
|
||
const sign2 = totPnl >= 0 ? '+' : '';
|
||
const cls = totPnl >= 0 ? 'pf-pnl-pos' : 'pf-pnl-neg';
|
||
document.getElementById('sum-pnl').innerHTML = `<span class="${cls}">${sign2}$${fmtNum(totPnl)}</span>`;
|
||
document.getElementById('sum-pct').innerHTML = `<span class="${cls}">${sign2}${totPct.toFixed(2)}%</span>`;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// ── Investment Simulator ─────────────────────────────────
|
||
|
||
function simLoad() {
|
||
return JSON.parse(localStorage.getItem(lsSIM()) || 'null') || {
|
||
capital: 10000, currency: 'USD',
|
||
assets: [],
|
||
scenarios: { bear: -20, base: 8, bull: 25 },
|
||
horizon: '1y'
|
||
};
|
||
}
|
||
function simSave(d) { localStorage.setItem(lsSIM(), JSON.stringify(d)); }
|
||
|
||
function simRender() {
|
||
const d = simLoad();
|
||
const capEl = document.getElementById('sim-capital');
|
||
const curEl = document.getElementById('sim-currency');
|
||
const bearEl = document.getElementById('sim-bear');
|
||
const baseEl = document.getElementById('sim-base');
|
||
const bullEl = document.getElementById('sim-bull');
|
||
if (capEl) capEl.value = d.capital;
|
||
if (curEl) curEl.value = d.currency;
|
||
if (bearEl) bearEl.value = d.scenarios.bear;
|
||
if (baseEl) baseEl.value = d.scenarios.base;
|
||
if (bullEl) bullEl.value = d.scenarios.bull;
|
||
document.querySelectorAll('.sim-hbtn').forEach(b => {
|
||
b.classList.toggle('active', b.dataset.h === d.horizon);
|
||
});
|
||
simRenderAssets(d);
|
||
simCalcReturns(d);
|
||
}
|
||
|
||
function simRenderAssets(d) {
|
||
const capital = parseFloat(d.capital) || 0;
|
||
const totalPct = d.assets.reduce((s, a) => s + (parseFloat(a.pct)||0), 0);
|
||
const pctEl = document.getElementById('sim-pct-total');
|
||
if (pctEl) {
|
||
pctEl.textContent = `已分配:${totalPct.toFixed(1)}%`;
|
||
pctEl.className = 'sim-pct-total' + (totalPct > 100 ? ' over' : '');
|
||
}
|
||
if (!d.assets.length) {
|
||
['sim-assets-wrap','sim-alloc-bar','sim-scenario-section','sim-returns-wrap'].forEach(id => {
|
||
const el = document.getElementById(id);
|
||
if (el) el.style.display = 'none';
|
||
});
|
||
return;
|
||
}
|
||
['sim-assets-wrap','sim-scenario-section','sim-returns-wrap'].forEach(id => {
|
||
const el = document.getElementById(id);
|
||
if (el) el.style.display = 'block';
|
||
});
|
||
const bar = document.getElementById('sim-alloc-bar');
|
||
if (bar) {
|
||
bar.style.display = 'flex';
|
||
bar.innerHTML = d.assets.map((a, i) => {
|
||
const w = Math.min(parseFloat(a.pct)||0, 100);
|
||
return `<div class="sim-alloc-seg" title="${a.symbol} ${a.pct}%" style="width:${w}%;background:hsl(${i*47},65%,50%);"></div>`;
|
||
}).join('');
|
||
}
|
||
const tbody = document.getElementById('sim-assets-tbody');
|
||
if (tbody) {
|
||
const cur = d.currency === 'CNY' ? '¥' : '$';
|
||
tbody.innerHTML = d.assets.map((a, idx) => {
|
||
const invest = capital * (parseFloat(a.pct)||0) / 100;
|
||
return `<tr>
|
||
<td><strong>${a.symbol}</strong></td>
|
||
<td>${a.alias || '—'}</td>
|
||
<td><input type="number" min="0" max="100" step="1" value="${a.pct}" onchange="simUpdatePct(${idx},this.value)" style="width:55px;background:transparent;border:1px solid var(--border);color:var(--text);border-radius:3px;padding:2px 5px;" /></td>
|
||
<td>${cur}${fmtNum(invest)}</td>
|
||
<td style="font-size:0.72rem;color:var(--dimmed)">${a.entryPrice ? cur + fmtNum(a.entryPrice) : '—'}</td>
|
||
<td><button class="wl-rm" onclick="simRemoveAsset(${idx})">×</button></td>
|
||
</tr>`;
|
||
}).join('');
|
||
}
|
||
}
|
||
|
||
async function simCalcReturns(d) {
|
||
const tbody = document.getElementById('sim-returns-tbody');
|
||
if (!tbody || !d.assets.length) return;
|
||
const capital = parseFloat(d.capital) || 0;
|
||
const horizonMap = { '1m': 1/12, '3m': 0.25, '6m': 0.5, '1y': 1, '3y': 3, '5y': 5 };
|
||
const years = horizonMap[d.horizon] || 1;
|
||
const bear = parseFloat(d.scenarios.bear) / 100;
|
||
const base = parseFloat(d.scenarios.base) / 100;
|
||
const bull = parseFloat(d.scenarios.bull) / 100;
|
||
const cur = d.currency === 'CNY' ? '¥' : '$';
|
||
function calcProfit(invest, rate, yrs) { return invest * Math.pow(1 + rate, yrs) - invest; }
|
||
function sCell(v, invest) {
|
||
const p = invest ? (v / invest * 100).toFixed(1) : '0.0';
|
||
const sign = v >= 0 ? '+' : '';
|
||
const cls = v >= 0 ? 'sim-ret-pos' : 'sim-ret-neg';
|
||
return `<span class="${cls}">${sign}${cur}${fmtNum(Math.abs(v))}<br><small>${sign}${p}%</small></span>`;
|
||
}
|
||
let totBear = 0, totBase = 0, totBull = 0, totInvest = 0;
|
||
// Render scenario columns immediately
|
||
tbody.innerHTML = d.assets.map(a => {
|
||
const invest = capital * (parseFloat(a.pct)||0) / 100;
|
||
const pb = calcProfit(invest, bear, years);
|
||
const pn = calcProfit(invest, base, years);
|
||
const pu = calcProfit(invest, bull, years);
|
||
totBear += pb; totBase += pn; totBull += pu; totInvest += invest;
|
||
const rid = 'simreal-' + a.symbol.replace(/[^a-z0-9]/gi,'_');
|
||
const realCell = a.entryPrice
|
||
? `<td id="${rid}"><span style="color:var(--dimmed);font-size:0.72rem">拉取中…</span></td>`
|
||
: `<td style="color:var(--dimmed);font-size:0.72rem">无买入价</td>`;
|
||
return `<tr>
|
||
<td><strong>${a.symbol}</strong>${a.alias ? `<br><small style="color:var(--muted)">${a.alias}</small>` : ''}</td>
|
||
<td>${cur}${fmtNum(invest)}</td>
|
||
${realCell}
|
||
<td>${sCell(pb, invest)}</td>
|
||
<td>${sCell(pn, invest)}</td>
|
||
<td>${sCell(pu, invest)}</td>
|
||
</tr>`;
|
||
}).join('');
|
||
tbody.innerHTML += `<tr class="total-row">
|
||
<td>合计</td>
|
||
<td>${cur}${fmtNum(totInvest)}</td>
|
||
<td id="simreal-total">—</td>
|
||
<td>${sCell(totBear, totInvest)}</td>
|
||
<td>${sCell(totBase, totInvest)}</td>
|
||
<td>${sCell(totBull, totInvest)}</td>
|
||
</tr>`;
|
||
// Async: fetch live prices for real P&L column
|
||
const realResults = new Array(d.assets.length).fill(null);
|
||
let fetched = 0;
|
||
d.assets.forEach(async (a, idx) => {
|
||
if (!a.entryPrice) { fetched++; return; }
|
||
const rid = 'simreal-' + a.symbol.replace(/[^a-z0-9]/gi,'_');
|
||
const el = document.getElementById(rid);
|
||
const info = await fetchPrice(a.symbol).catch(() => null);
|
||
fetched++;
|
||
if (!el) return;
|
||
if (!info) { el.innerHTML = '<span style="color:var(--dimmed);font-size:0.72rem">获取失败</span>'; return; }
|
||
const invest = capital * (parseFloat(a.pct)||0) / 100;
|
||
const realPnl = invest * (info.price - a.entryPrice) / a.entryPrice;
|
||
realResults[idx] = realPnl;
|
||
el.innerHTML = sCell(realPnl, invest);
|
||
// Update total row when all fetched
|
||
if (fetched === d.assets.length) {
|
||
const totEl = document.getElementById('simreal-total');
|
||
if (totEl) {
|
||
const validResults = realResults.filter(r => r !== null);
|
||
if (validResults.length) {
|
||
const totReal = validResults.reduce((s,v) => s+v, 0);
|
||
totEl.innerHTML = sCell(totReal, totInvest);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function simUpdate() {
|
||
const d = simLoad();
|
||
d.capital = parseFloat(document.getElementById('sim-capital').value) || 10000;
|
||
d.currency = document.getElementById('sim-currency').value;
|
||
d.scenarios.bear = parseFloat(document.getElementById('sim-bear').value) || -20;
|
||
d.scenarios.base = parseFloat(document.getElementById('sim-base').value) || 8;
|
||
d.scenarios.bull = parseFloat(document.getElementById('sim-bull').value) || 25;
|
||
simSave(d);
|
||
simRenderAssets(d);
|
||
simCalcReturns(d);
|
||
}
|
||
|
||
function simSetHorizon(btn, h) {
|
||
const d = simLoad();
|
||
d.horizon = h;
|
||
simSave(d);
|
||
document.querySelectorAll('.sim-hbtn').forEach(b => b.classList.toggle('active', b.dataset.h === h));
|
||
simCalcReturns(d);
|
||
}
|
||
|
||
async function simAddAsset() {
|
||
const sym = (document.getElementById('sim-sym').value || '').trim().toUpperCase();
|
||
const alias = (document.getElementById('sim-alias').value || '').trim();
|
||
const pct = parseFloat(document.getElementById('sim-pct').value) || 0;
|
||
const manualEntry = parseFloat(document.getElementById('sim-entry').value) || null;
|
||
if (!sym) return;
|
||
const d = simLoad();
|
||
if (d.assets.find(a => a.symbol === sym)) { alert('该资产已在列表中'); return; }
|
||
let entryPrice = manualEntry;
|
||
if (!entryPrice) {
|
||
const btn = document.querySelector('#panel-sim .sim-add-row button');
|
||
if (btn) { btn.textContent = '抓取中…'; btn.disabled = true; }
|
||
const info = await fetchPrice(sym).catch(() => null);
|
||
if (btn) { btn.textContent = '+ 添加'; btn.disabled = false; }
|
||
entryPrice = info ? info.price : null;
|
||
if (!entryPrice) { alert(`无法获取 ${sym} 当前价格,请手动填入买入价`); return; }
|
||
}
|
||
d.assets.push({ symbol: sym, alias, pct, entryPrice, entryDate: new Date().toISOString().slice(0,10) });
|
||
simSave(d);
|
||
document.getElementById('sim-sym').value = '';
|
||
document.getElementById('sim-alias').value = '';
|
||
document.getElementById('sim-pct').value = '';
|
||
document.getElementById('sim-entry').value = '';
|
||
simRender();
|
||
}
|
||
|
||
function simRemoveAsset(idx) {
|
||
const d = simLoad();
|
||
d.assets.splice(idx, 1);
|
||
simSave(d);
|
||
simRender();
|
||
}
|
||
|
||
function simUpdatePct(idx, val) {
|
||
const d = simLoad();
|
||
d.assets[idx].pct = parseFloat(val) || 0;
|
||
simSave(d);
|
||
simRenderAssets(d);
|
||
simCalcReturns(d);
|
||
}
|
||
|
||
function simReset() {
|
||
if (!confirm('确认重置模拟器数据?')) return;
|
||
localStorage.removeItem(lsSIM());
|
||
simRender();
|
||
}
|
||
|
||
// Init personal dashboard on load
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
profileRenderSelect();
|
||
wlRender();
|
||
pfRender();
|
||
simRender();
|
||
// Enter key triggers add
|
||
document.getElementById('wl-name').addEventListener('keydown', e => { if(e.key==='Enter') wlAdd(); });
|
||
document.getElementById('pf-cost').addEventListener('keydown', e => { if(e.key==='Enter') pfAdd(); });
|
||
});
|
||
|
||
|
||
|
||
// ── Reading progress bar ──────────────────────────────────────────────────────────
|
||
(function() {
|
||
const bar = document.getElementById('reading-progress');
|
||
if (!bar) return;
|
||
window.addEventListener('scroll', function() {
|
||
const scrolled = window.scrollY;
|
||
const total = document.documentElement.scrollHeight - window.innerHeight;
|
||
bar.style.width = (total > 0 ? (scrolled / total * 100) : 0) + '%';
|
||
}, { passive: true });
|
||
})();
|
||
|
||
// ── Back-to-top FAB visibility ───────────────────────────────────────────────────
|
||
(function() {
|
||
const btn = document.getElementById('fab-top');
|
||
if (!btn) return;
|
||
window.addEventListener('scroll', function() {
|
||
btn.classList.toggle('visible', window.scrollY > 400);
|
||
}, { passive: true });
|
||
})();
|
||
|
||
// ── Share / copy URL ────────────────────────────────────────────────────────────
|
||
function shareOrCopy() {
|
||
const url = window.location.href;
|
||
const toast = document.getElementById('share-toast');
|
||
if (navigator.share) {
|
||
navigator.share({ title: '金融日报 | Financial Daily', url });
|
||
} else {
|
||
navigator.clipboard.writeText(url).then(function() {
|
||
toast.classList.add('show');
|
||
setTimeout(function() { toast.classList.remove('show'); }, 2000);
|
||
});
|
||
}
|
||
}
|
||
|
||
// ── Jump nav active highlight on scroll ────────────────────────────────────────
|
||
(function() {
|
||
const sections = document.querySelectorAll('.sec-anchor[id]');
|
||
const links = document.querySelectorAll('.jump-nav a');
|
||
if (!sections.length || !links.length) return;
|
||
const obs = new IntersectionObserver(function(entries) {
|
||
entries.forEach(function(entry) {
|
||
if (entry.isIntersecting) {
|
||
links.forEach(function(l) { l.classList.remove('active'); });
|
||
const a = document.querySelector('.jump-nav a[href="#' + entry.target.id + '"]');
|
||
if (a) a.classList.add('active');
|
||
}
|
||
});
|
||
}, { rootMargin: '-20% 0px -70% 0px' });
|
||
sections.forEach(function(s) { obs.observe(s); });
|
||
})();
|
||
|
||
// ── Dismiss subscribe strip if already dismissed ────────────────────────────
|
||
(function() {
|
||
if (localStorage.getItem('fd_sub_dismissed')) {
|
||
var el = document.getElementById('subscribe-strip');
|
||
if (el) el.style.display = 'none';
|
||
}
|
||
})();
|
||
|
||
// ── Subscriber count (Telegram) ───────────────────────────────────────
|
||
(function loadSubCount() {
|
||
const el = document.getElementById('subscriber-count');
|
||
if (!el) return;
|
||
fetch('data/investment-subscribers.json')
|
||
.then(r => r.json())
|
||
.then(d => {
|
||
const count = Object.values(d.subscribers || {}).filter(s => s.active).length;
|
||
if (count > 0) el.textContent = `📱 ${count} 人已订阅Telegram早报`;
|
||
})
|
||
.catch(() => {});
|
||
})();
|
||
|
||
// ── AI 操盘实验(多模型对比)─────────────────────────────────────────
|
||
(function loadAIPortfolio() {
|
||
const fmt$ = v => '$' + v.toLocaleString('en-US', {minimumFractionDigits:2, maximumFractionDigits:2});
|
||
const fmtPct = v => (v >= 0 ? '+' : '') + v.toFixed(2) + '%';
|
||
|
||
function renderModelDetail(modelId, pf, contest) {
|
||
const history = pf.history || [];
|
||
const latest = history[history.length - 1] || {};
|
||
const holdings = pf.holdings || [];
|
||
const startCap = contest.start_capital || 10000;
|
||
const pv = latest.portfolio_value || 0;
|
||
const bv = latest.baseline_value || 0;
|
||
const cash = latest.cash || 0;
|
||
const positions = latest.positions || {};
|
||
const trades = latest.trades || [];
|
||
|
||
let html = '';
|
||
|
||
// 持仓明细
|
||
html += `<div style="font-size:0.72rem;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);margin-bottom:8px;">📊 当前持仓</div>`;
|
||
html += `<div style="display:flex;flex-direction:column;gap:5px;margin-bottom:14px;">`;
|
||
Object.entries(positions).forEach(([t, pos]) => {
|
||
const h = holdings.find(x => x.ticker === t);
|
||
const avg = h ? h.avg_cost : pos.price;
|
||
const pl = pos.price - avg;
|
||
const plP = avg > 0 ? pl / avg * 100 : 0;
|
||
const pct = pv > 0 ? pos.value / pv * 100 : 0;
|
||
const c = pl >= 0 ? 'var(--up)' : 'var(--down)';
|
||
html += `<div style="display:flex;align-items:center;gap:8px;padding:5px 8px;background:rgba(255,255,255,0.03);border-radius:6px;font-size:0.79rem;">
|
||
<span style="font-family:var(--mono);font-weight:600;min-width:44px;color:var(--accent);">${t}</span>
|
||
<span style="color:var(--muted);min-width:46px;">${pos.shares}股</span>
|
||
<span style="font-family:var(--mono);min-width:66px;">$${pos.price.toFixed(2)}</span>
|
||
<span style="font-family:var(--mono);min-width:72px;">${fmt$(pos.value)}</span>
|
||
<span style="color:${c};min-width:62px;">${(pl>=0?'+':'')}${fmtPct(plP)}</span>
|
||
<div style="flex:1;height:3px;background:rgba(255,255,255,0.06);border-radius:2px;"><div style="width:${Math.min(pct,100).toFixed(1)}%;height:100%;background:var(--accent);border-radius:2px;"></div></div>
|
||
<span style="color:var(--muted);min-width:34px;text-align:right;">${pct.toFixed(1)}%</span>
|
||
</div>`;
|
||
});
|
||
if (cash > 0) {
|
||
const cp = pv > 0 ? cash / pv * 100 : 0;
|
||
html += `<div style="display:flex;align-items:center;gap:8px;padding:5px 8px;background:rgba(255,255,255,0.03);border-radius:6px;font-size:0.79rem;">
|
||
<span style="font-family:var(--mono);font-weight:600;min-width:44px;color:#ffb300;">CASH</span>
|
||
<span style="color:var(--muted);min-width:46px;">—</span>
|
||
<span style="font-family:var(--mono);min-width:66px;">—</span>
|
||
<span style="font-family:var(--mono);min-width:72px;">${fmt$(cash)}</span>
|
||
<span style="color:var(--muted);min-width:62px;">现金</span>
|
||
<div style="flex:1;height:3px;background:rgba(255,255,255,0.06);border-radius:2px;"><div style="width:${Math.min(cp,100).toFixed(1)}%;height:100%;background:#ffb300;border-radius:2px;"></div></div>
|
||
<span style="color:var(--muted);min-width:34px;text-align:right;">${cp.toFixed(1)}%</span>
|
||
</div>`;
|
||
}
|
||
html += '</div>';
|
||
|
||
// 交易记录
|
||
if (trades.length) {
|
||
html += `<div style="font-size:0.72rem;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);margin-bottom:8px;">📋 今日交易</div>`;
|
||
html += trades.map(t => {
|
||
const ic = t.action==='BUY'?'🟢':t.action==='SELL'?'🔴':'🟡';
|
||
const c2 = t.action==='BUY'?'var(--up)':t.action==='SELL'?'var(--down)':'var(--warn)';
|
||
return `<div style="padding:5px 0;border-bottom:1px solid var(--border);font-size:0.8rem;">${ic} <strong style="color:${c2};">${t.action} ${t.ticker}</strong> ${t.shares}股 @ $${(t.price||0).toFixed(2)} <span style="color:var(--muted);">${t.reason||''}</span></div>`;
|
||
}).join('');
|
||
html += '<div style="margin-bottom:12px;"></div>';
|
||
}
|
||
|
||
// 备注
|
||
if (latest.note) {
|
||
html += `<div style="font-size:0.8rem;color:var(--muted);padding:8px 10px;background:rgba(0,212,170,0.04);border-left:2px solid var(--accent);border-radius:4px;">${latest.note}</div>`;
|
||
}
|
||
|
||
document.getElementById('ai-model-detail').innerHTML = html;
|
||
}
|
||
|
||
fetch('data/ai-portfolio.json?v=' + Date.now())
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
const contest = data.contest || {};
|
||
const portfolios = data.portfolios || {};
|
||
const startCap = contest.start_capital || 10000;
|
||
const startDate = new Date(contest.start_date || '2026-05-13');
|
||
|
||
// 计算排名
|
||
const ranked = Object.entries(portfolios).map(([id, pf]) => {
|
||
const hist = pf.history || [];
|
||
const latest = hist[hist.length - 1] || {};
|
||
const pv = latest.portfolio_value || startCap;
|
||
const bv = latest.baseline_value || startCap;
|
||
const d = new Date(latest.date || contest.start_date);
|
||
const days = Math.round((d - startDate) / 86400000) + 1;
|
||
return { id, pf, pv, bv, days, latest };
|
||
}).sort((a, b) => b.pv - a.pv);
|
||
|
||
// 排行榜
|
||
const lbEl = document.getElementById('ai-leaderboard');
|
||
// 标题行:说明这是累计净值
|
||
lbEl.innerHTML = `<div style="font-size:0.72rem;color:var(--muted);margin-bottom:8px;">📅 累计净值 — 初始资金 ${fmt$(startCap)},自 ${contest.start_date} 起</div>`;
|
||
lbEl.innerHTML += ranked.map((r, i) => {
|
||
const pv = r.pv;
|
||
const bv = r.bv;
|
||
const pnl = pv - startCap;
|
||
const ret = pnl / startCap * 100;
|
||
const alpha = pv - bv;
|
||
const alphaP = bv > 0 ? alpha / bv * 100 : 0;
|
||
const retC = ret >= 0 ? 'var(--up)' : 'var(--down)';
|
||
const alpC = alpha >= 0 ? 'var(--up)' : 'var(--down)';
|
||
const medal = i===0 ? '🥇' : i===1 ? '🥈' : '🥉';
|
||
const pf = r.pf;
|
||
return `<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:8px;">
|
||
<span style="font-size:1.2rem;min-width:28px;">${medal}</span>
|
||
<span style="font-size:1.1rem;">${pf.emoji||'🤖'}</span>
|
||
<div style="flex:1;">
|
||
<div style="font-weight:600;font-size:0.9rem;">${pf.display_name||r.id}</div>
|
||
<div style="font-size:0.75rem;color:var(--muted);">${r.latest.note||''}</div>
|
||
</div>
|
||
<div style="text-align:right;min-width:90px;">
|
||
<div style="font-family:var(--mono);font-weight:600;font-size:0.95rem;">${fmt$(pv)}</div>
|
||
<div style="font-size:0.72rem;color:var(--muted);">累计净值</div>
|
||
</div>
|
||
<div style="text-align:right;min-width:90px;">
|
||
<div style="font-family:var(--mono);font-size:0.9rem;font-weight:600;color:${retC};">${(pnl>=0?'+':'')}${fmt$(pnl)}</div>
|
||
<div style="font-size:0.72rem;color:${retC};">${(ret>=0?'+':'')}${ret.toFixed(2)}% 累计</div>
|
||
</div>
|
||
<div style="text-align:right;min-width:80px;">
|
||
<div style="font-family:var(--mono);font-size:0.85rem;color:${alpC};">${(alpha>=0?'+':'')}${fmt$(alpha)}</div>
|
||
<div style="font-size:0.75rem;color:var(--muted);">vs SPY</div>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
// SPY 基线行
|
||
const anyLatest = ranked[0]?.latest || {};
|
||
const spyVal = anyLatest.baseline_value || startCap;
|
||
const spyPnl = spyVal - startCap;
|
||
const spyRet = spyPnl / startCap * 100;
|
||
const spyC = spyRet >= 0 ? 'var(--up)' : 'var(--down)';
|
||
lbEl.innerHTML += `<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;background:rgba(255,255,255,0.02);border:1px solid var(--border);border-radius:8px;opacity:0.7;">
|
||
<span style="font-size:1.2rem;min-width:28px;">📊</span>
|
||
<span style="font-size:1.1rem;">—</span>
|
||
<div style="flex:1;"><div style="font-weight:600;font-size:0.9rem;">SPY 基线(买入持有)</div><div style="font-size:0.75rem;color:var(--muted);">S&P 500 ETF · ${contest.baseline?.shares||13.5296} 股</div></div>
|
||
<div style="text-align:right;min-width:90px;">
|
||
<div style="font-family:var(--mono);font-weight:600;font-size:0.95rem;">${fmt$(spyVal)}</div>
|
||
<div style="font-size:0.72rem;color:var(--muted);">累计净值</div>
|
||
</div>
|
||
<div style="text-align:right;min-width:90px;">
|
||
<div style="font-family:var(--mono);font-size:0.9rem;font-weight:600;color:${spyC};">${(spyPnl>=0?'+':'')}${fmt$(spyPnl)}</div>
|
||
<div style="font-size:0.72rem;color:${spyC};">${(spyRet>=0?'+':'')}${spyRet.toFixed(2)}% 累计</div>
|
||
</div>
|
||
<div style="text-align:right;min-width:80px;">
|
||
<div style="font-family:var(--mono);font-size:0.85rem;color:var(--muted);">—</div>
|
||
<div style="font-size:0.75rem;color:var(--muted);">基准</div>
|
||
</div>
|
||
</div>`;
|
||
|
||
// 模型 Tab
|
||
const tabsEl = document.getElementById('ai-model-tabs');
|
||
let activeModel = ranked[0]?.id;
|
||
function setActiveTab(id) {
|
||
activeModel = id;
|
||
tabsEl.querySelectorAll('button').forEach(b => {
|
||
b.style.background = b.dataset.id === id ? 'rgba(0,212,170,0.15)' : 'rgba(255,255,255,0.04)';
|
||
b.style.borderColor = b.dataset.id === id ? 'var(--accent)' : 'var(--border)';
|
||
b.style.color = b.dataset.id === id ? 'var(--accent)' : 'var(--text)';
|
||
});
|
||
const pf = portfolios[id];
|
||
if (pf) renderModelDetail(id, pf, contest);
|
||
}
|
||
tabsEl.innerHTML = ranked.map(r => {
|
||
const pf = r.pf;
|
||
return `<button data-id="${r.id}" onclick="(function(id){window._aiSetTab&&window._aiSetTab(id)})('${r.id}')" style="padding:6px 12px;border-radius:6px;border:1px solid var(--border);background:rgba(255,255,255,0.04);color:var(--text);cursor:pointer;font-size:0.8rem;">${pf.emoji||'🤖'} ${pf.display_name||r.id}</button>`;
|
||
}).join('');
|
||
window._aiSetTab = setActiveTab;
|
||
setActiveTab(activeModel);
|
||
|
||
// 历史对比表
|
||
const histEl = document.getElementById('ai-history-table');
|
||
const allDates = [...new Set(ranked.flatMap(r => (r.pf.history||[]).map(h => h.date)))].sort().reverse();
|
||
if (allDates.length > 1) {
|
||
const headers = ['<th style="text-align:left;padding:4px 6px;font-size:0.72rem;">日期</th>',
|
||
...ranked.map(r => `<th style="text-align:right;padding:4px 6px;font-size:0.72rem;">${r.pf.emoji||'📊'} ${r.pf.display_name||r.id}</th>`),
|
||
'<th style="text-align:right;padding:4px 6px;font-size:0.72rem;">SPY 基线</th>'
|
||
].join('');
|
||
const subHeaders = ['<th style="text-align:left;padding:2px 6px;font-size:0.65rem;color:var(--muted);">日期</th>',
|
||
...ranked.map(() => '<th style="text-align:right;padding:2px 6px;font-size:0.65rem;color:var(--muted);">净值 / 今日盈亏</th>'),
|
||
'<th style="text-align:right;padding:2px 6px;font-size:0.65rem;color:var(--muted);">净值 / 今日盈亏</th>'
|
||
].join('');
|
||
const rows = allDates.map((date, dateIndex) => {
|
||
const prevDate = allDates[dateIndex + 1]; // previous trading day (sorted descending)
|
||
const cells = ranked.map(r => {
|
||
const rec = (r.pf.history||[]).find(h => h.date === date);
|
||
if (!rec) return '<td style="text-align:right;padding:4px 6px;color:var(--muted);">—</td>';
|
||
const pv = rec.portfolio_value;
|
||
const prevRec = prevDate ? (r.pf.history||[]).find(h => h.date === prevDate) : null;
|
||
const prevVal = prevRec ? prevRec.portfolio_value : startCap;
|
||
const dailyPnl = pv - prevVal;
|
||
const dailyRet = dailyPnl / prevVal * 100;
|
||
const c = dailyRet >= 0 ? 'var(--up)' : 'var(--down)';
|
||
return `<td style="text-align:right;padding:4px 6px;"><div style="font-family:var(--mono);font-size:0.8rem;font-weight:600;">${fmt$(pv)}</div><div style="font-size:0.72rem;color:${c};">${(dailyPnl>=0?'+':'')}${fmt$(dailyPnl)} (${(dailyRet>=0?'+':'')}${dailyRet.toFixed(2)}% 今日)</div></td>`;
|
||
}).join('');
|
||
const spyRec = (ranked[0]?.pf.history||[]).find(h => h.date === date);
|
||
const spyV = spyRec ? spyRec.baseline_value : startCap;
|
||
const spyPrevRec = prevDate ? (ranked[0]?.pf.history||[]).find(h => h.date === prevDate) : null;
|
||
const spyPrevV = spyPrevRec ? spyPrevRec.baseline_value : startCap;
|
||
const spyDailyPnl = spyV - spyPrevV;
|
||
const spyDailyR = spyDailyPnl / spyPrevV * 100;
|
||
const sc = spyDailyR >= 0 ? 'var(--up)' : 'var(--down)';
|
||
return `<tr style="border-top:1px solid var(--border);"><td style="padding:4px 6px;color:var(--muted);font-size:0.8rem;">${date}</td>${cells}<td style="text-align:right;padding:4px 6px;"><div style="font-family:var(--mono);font-size:0.8rem;font-weight:600;">${fmt$(spyV)}</div><div style="font-size:0.72rem;color:${sc};">${(spyDailyPnl>=0?'+':'')}${fmt$(spyDailyPnl)} (${(spyDailyR>=0?'+':'')}${spyDailyR.toFixed(2)}% 今日)</div></td></tr>`;
|
||
}).join('');
|
||
histEl.innerHTML = `<div style="font-size:0.72rem;color:var(--muted);margin-bottom:6px;">📈 净值走势(自 ${contest.start_date} 起,初始 ${fmt$(startCap)})</div><table style="width:100%;border-collapse:collapse;"><thead><tr style="color:var(--text);border-bottom:1px solid var(--border);">${headers}</tr></thead><tbody>${rows}</tbody></table>`;
|
||
} else {
|
||
histEl.innerHTML = '<span style="color:var(--muted);font-size:0.8rem;">天数超过1天后显示对比曲线…</span>';
|
||
}
|
||
|
||
document.getElementById('ai-portfolio-loading').style.display='none';
|
||
document.getElementById('ai-portfolio-content').style.display='';
|
||
})
|
||
.catch(()=>{ const el=document.getElementById('ai-portfolio-loading'); if(el) el.textContent='数据加载失败'; });
|
||
})();
|
||
|
||
// ── Visitor counter (Cloudflare Worker + KV) ──────────────────────────
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
const WORKER = 'https://finance-daily-counter.yangbai0110.workers.dev';
|
||
const LS_KEY = 'fd_visited';
|
||
|
||
const visEl = document.getElementById('footer-visitors');
|
||
const pvEl = document.getElementById('footer-pageviews');
|
||
|
||
function fmt(n) { return Number(n).toLocaleString('en-US'); }
|
||
|
||
function animate(el, target) {
|
||
if (!el) return;
|
||
if (target === 0) { el.textContent = '0'; return; }
|
||
const dur = 900; let t0 = null;
|
||
const step = ts => {
|
||
if (!t0) t0 = ts;
|
||
const p = Math.min((ts - t0) / dur, 1);
|
||
el.textContent = fmt(Math.round((1 - Math.pow(1 - p, 3)) * target));
|
||
if (p < 1) requestAnimationFrame(step);
|
||
};
|
||
requestAnimationFrame(step);
|
||
}
|
||
|
||
const isNew = !localStorage.getItem(LS_KEY);
|
||
fetch(WORKER + (isNew ? '?newVisitor=1' : '?newVisitor=0'))
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
localStorage.setItem(LS_KEY, '1');
|
||
animate(visEl, data.visitors || 0);
|
||
animate(pvEl, data.pageViews || 0);
|
||
})
|
||
.catch(() => {
|
||
if (visEl) visEl.textContent = '--';
|
||
if (pvEl) pvEl.textContent = '--';
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<script>
|
||
// ─── Streak counter ────────────────────────────────────────────────────────
|
||
(function() {
|
||
var today = new Date().toISOString().slice(0,10);
|
||
var last = localStorage.getItem('fd_last_visit') || '';
|
||
var streak = parseInt(localStorage.getItem('fd_streak') || '0');
|
||
if (last !== today) {
|
||
var yesterday = new Date(Date.now() - 86400000).toISOString().slice(0,10);
|
||
streak = (last === yesterday) ? streak + 1 : 1;
|
||
localStorage.setItem('fd_last_visit', today);
|
||
localStorage.setItem('fd_streak', streak);
|
||
}
|
||
if (streak >= 2) {
|
||
var meta = document.querySelector('.header-meta');
|
||
if (meta) {
|
||
var sb = document.createElement('span');
|
||
sb.className = 'streak-badge';
|
||
sb.title = '连续访问天数';
|
||
sb.innerHTML = '🔥 ' + streak + ' 天';
|
||
meta.appendChild(sb);
|
||
}
|
||
}
|
||
})();
|
||
|
||
// ─── Mark first breaking news as NEW ────────────────────────────────────────
|
||
document.querySelectorAll('.news-item.breaking, .news-item.major').forEach(function(n, i) {
|
||
if (i < 2) {
|
||
var title = n.querySelector('.news-title');
|
||
if (title) {
|
||
var badge = document.createElement('span');
|
||
badge.className = 'new-badge';
|
||
badge.textContent = '新';
|
||
badge.style.cssText = 'display:inline-block;font-size:.58rem;font-family:var(--mono);padding:1px 5px;border-radius:3px;background:rgba(0,212,170,.15);color:var(--accent);border:1px solid rgba(0,212,170,.3);margin-right:6px;vertical-align:middle;';
|
||
title.insertBefore(badge, title.firstChild);
|
||
}
|
||
}
|
||
});
|
||
|
||
// ─── News toggle ────────────────────────────────────────────────────────────
|
||
function toggleNews(el) { el.classList.toggle('expanded'); }
|
||
</script>
|
||
|
||
<!-- Floating action cluster -->
|
||
<div class="fab-cluster">
|
||
<button class="fab" id="fab-share" title="分享此页" onclick="shareOrCopy()">🔗</button>
|
||
<button class="fab" id="fab-top" title="回到顶部" onclick="window.scrollTo({top:0,behavior:'smooth'})">↑</button>
|
||
</div>
|
||
<div id="share-toast">✅ 链接已复制</div>
|
||
|
||
<!-- =======================================================
|
||
INTRO ANIMATION
|
||
======================================================= -->
|
||
<script>
|
||
(function() {
|
||
'use strict';
|
||
// Skip if user already visited this session
|
||
if (sessionStorage.getItem('fd-intro-done')) {
|
||
var ov = document.getElementById('intro-overlay');
|
||
if (ov) ov.classList.add('gone');
|
||
return;
|
||
}
|
||
|
||
var canvas = document.getElementById('intro-canvas');
|
||
var overlay = document.getElementById('intro-overlay');
|
||
if (!canvas || !overlay) return;
|
||
|
||
var ctx = canvas.getContext('2d');
|
||
var W, H;
|
||
|
||
function resize() {
|
||
W = canvas.width = window.innerWidth;
|
||
H = canvas.height = window.innerHeight;
|
||
}
|
||
resize();
|
||
window.addEventListener('resize', resize);
|
||
|
||
// --- Candlestick data (simulated) ---
|
||
var candles = [];
|
||
for (var i = 0; i < 60; i++) {
|
||
var base = 400 + Math.sin(i * 0.18) * 40 + Math.random() * 20;
|
||
var o = base + (Math.random() - 0.5) * 15;
|
||
var c = base + (Math.random() - 0.5) * 15;
|
||
var h = Math.max(o, c) + Math.random() * 12;
|
||
var l = Math.min(o, c) - Math.random() * 12;
|
||
candles.push({ o: o, c: c, h: h, l: l });
|
||
}
|
||
|
||
// --- Streaming ticker lines ---
|
||
var tickers = [
|
||
'NVDA +2.47%', 'SPY +0.31%', 'QQQ +0.19%', 'AAPL -0.12%', 'TSLA +1.04%',
|
||
'META +0.88%', 'MSFT +0.55%', 'BTC +0.24%', 'GLD -0.42%', 'AMD +3.21%',
|
||
'GOOGL +0.37%', 'AMZN +0.62%', 'TLT -0.35%', 'INTC +4.60%', 'AVGO +1.14%',
|
||
'VIX 16.76', 'DXY 99.25', 'WTI $98.05', 'AU $4524', 'ETH +1.18%'
|
||
];
|
||
var streamLines = [];
|
||
for (var j = 0; j < 18; j++) {
|
||
streamLines.push({
|
||
x: Math.random() * W,
|
||
y: Math.random() * H,
|
||
speed: 0.3 + Math.random() * 0.5,
|
||
text: tickers[j % tickers.length],
|
||
opacity: 0.04 + Math.random() * 0.12,
|
||
size: 10 + Math.floor(Math.random() * 4)
|
||
});
|
||
}
|
||
|
||
var progress = document.getElementById('intro-progress');
|
||
var startTime = performance.now();
|
||
var DURATION = 3500; // ms total
|
||
var animDone = false;
|
||
var rafId;
|
||
|
||
function draw(now) {
|
||
if (animDone) return;
|
||
var elapsed = now - startTime;
|
||
var t = Math.min(elapsed / DURATION, 1);
|
||
|
||
ctx.clearRect(0, 0, W, H);
|
||
|
||
// -- Background gradient --
|
||
var grad = ctx.createRadialGradient(W/2, H/2, 0, W/2, H/2, Math.max(W,H) * 0.7);
|
||
grad.addColorStop(0, 'rgba(7,20,35,.85)');
|
||
grad.addColorStop(1, 'rgba(3,6,9,1)');
|
||
ctx.fillStyle = grad;
|
||
ctx.fillRect(0, 0, W, H);
|
||
|
||
// -- Grid lines --
|
||
ctx.strokeStyle = 'rgba(0,212,170,0.04)';
|
||
ctx.lineWidth = 1;
|
||
var step = 60;
|
||
for (var gx = 0; gx < W; gx += step) {
|
||
ctx.beginPath(); ctx.moveTo(gx, 0); ctx.lineTo(gx, H); ctx.stroke();
|
||
}
|
||
for (var gy = 0; gy < H; gy += step) {
|
||
ctx.beginPath(); ctx.moveTo(0, gy); ctx.lineTo(W, gy); ctx.stroke();
|
||
}
|
||
|
||
// -- Streaming tickers --
|
||
ctx.font = '11px JetBrains Mono, monospace';
|
||
streamLines.forEach(function(l) {
|
||
l.y -= l.speed;
|
||
if (l.y < -20) { l.y = H + 20; l.x = Math.random() * W; l.text = tickers[Math.floor(Math.random()*tickers.length)]; }
|
||
var isUp = l.text.indexOf('-') === -1 || l.text.indexOf('+') !== -1;
|
||
ctx.fillStyle = isUp
|
||
? 'rgba(0,230,118,' + l.opacity + ')'
|
||
: 'rgba(255,82,82,' + l.opacity + ')';
|
||
ctx.fillText(l.text, l.x, l.y);
|
||
});
|
||
|
||
// -- Candlestick chart (bottom area) --
|
||
var revealCandles = Math.floor(t * candles.length);
|
||
var cW = Math.min(W * 0.7, 600);
|
||
var cH = 90;
|
||
var cX = (W - cW) / 2;
|
||
var cY = H * 0.72;
|
||
var maxP = Math.max.apply(null, candles.map(function(c){return c.h;}));
|
||
var minP = Math.min.apply(null, candles.map(function(c){return c.l;}));
|
||
var range = maxP - minP || 1;
|
||
var barW = cW / candles.length * 0.7;
|
||
|
||
ctx.globalAlpha = Math.min(t * 3, 1) * 0.7;
|
||
for (var ci = 0; ci < revealCandles; ci++) {
|
||
var cd = candles[ci];
|
||
var cx = cX + (ci + 0.5) * (cW / candles.length);
|
||
var cy_o = cY + (1 - (cd.o - minP) / range) * cH;
|
||
var cy_c = cY + (1 - (cd.c - minP) / range) * cH;
|
||
var cy_h = cY + (1 - (cd.h - minP) / range) * cH;
|
||
var cy_l = cY + (1 - (cd.l - minP) / range) * cH;
|
||
var isGreen = cd.c >= cd.o;
|
||
|
||
ctx.strokeStyle = isGreen ? '#00e676' : '#ff5252';
|
||
ctx.lineWidth = 1;
|
||
ctx.beginPath(); ctx.moveTo(cx, cy_h); ctx.lineTo(cx, cy_l); ctx.stroke();
|
||
|
||
ctx.fillStyle = isGreen ? '#00e676' : '#ff5252';
|
||
var bh = Math.abs(cy_c - cy_o);
|
||
ctx.fillRect(cx - barW/2, Math.min(cy_o, cy_c), barW, Math.max(bh, 1.5));
|
||
}
|
||
ctx.globalAlpha = 1;
|
||
|
||
// -- Progress bar --
|
||
if (progress) progress.style.width = (t * 100) + '%';
|
||
|
||
if (elapsed >= DURATION) {
|
||
dismissIntro();
|
||
return;
|
||
}
|
||
rafId = requestAnimationFrame(draw);
|
||
}
|
||
rafId = requestAnimationFrame(draw);
|
||
|
||
// -- Populate intro tickers from quotes.json --
|
||
fetch('data/quotes.json?' + Date.now())
|
||
.then(function(r){ return r.ok ? r.json() : null; })
|
||
.then(function(data) {
|
||
if (!data || !data.quotes) return;
|
||
var map = {};
|
||
data.quotes.forEach(function(q){ map[q.name] = q; });
|
||
var bindings = [
|
||
['it-spy', 'it-spy-chg', '标晥00'],
|
||
['it-qqq', 'it-qqq-chg', '纳斯达克'],
|
||
['it-btc', 'it-btc-chg', '比特币'],
|
||
['it-gld', 'it-gld-chg', '黄金现货'],
|
||
];
|
||
bindings.forEach(function(b){
|
||
var q = map[b[2]];
|
||
var el = document.getElementById(b[0]);
|
||
var chgEl = document.getElementById(b[1]);
|
||
if (!el || !q) return;
|
||
el.textContent = q.value || '—';
|
||
if (chgEl && q.change_pct) {
|
||
chgEl.textContent = (q.direction === 'up' ? '▲ ' : '▼ ') + q.change_pct;
|
||
chgEl.className = 'it-chg ' + q.direction;
|
||
}
|
||
});
|
||
}).catch(function(){});
|
||
|
||
window.dismissIntro = function() {
|
||
if (animDone) return;
|
||
animDone = true;
|
||
cancelAnimationFrame(rafId);
|
||
overlay.classList.add('fade-out');
|
||
sessionStorage.setItem('fd-intro-done', '1');
|
||
setTimeout(function(){ overlay.classList.add('gone'); }, 750);
|
||
};
|
||
|
||
// Keyboard shortcut: any key skips intro
|
||
document.addEventListener('keydown', function handler(e) {
|
||
dismissIntro();
|
||
document.removeEventListener('keydown', handler);
|
||
}, { once: true });
|
||
})();
|
||
</script>
|
||
|
||
<!-- =======================================================
|
||
I18N / LANGUAGE TOGGLE
|
||
======================================================= -->
|
||
<script>
|
||
(function() {
|
||
'use strict';
|
||
|
||
var LANG = {
|
||
zh: {
|
||
'nav-archive': '归档',
|
||
'nav-summary': '📊 大势',
|
||
'nav-ai-trading': '🤖 AI操盘',
|
||
'nav-news': '📰 要闻',
|
||
'nav-watchlist': '🎯 AI分析',
|
||
'nav-macro': '📈 宏观',
|
||
'nav-earnings': '💼 财报',
|
||
'nav-sectors': '🏭 行业',
|
||
'nav-debate': '⚔️ 辩论',
|
||
'nav-calendar': '📅 日历',
|
||
'nav-my': '⭐ 我的',
|
||
},
|
||
en: {
|
||
'nav-archive': 'Archive',
|
||
'nav-summary': '📊 Market',
|
||
'nav-ai-trading': '🤖 AI Trade',
|
||
'nav-news': '📰 News',
|
||
'nav-watchlist': '🎯 Watchlist',
|
||
'nav-macro': '📈 Macro',
|
||
'nav-earnings': '💼 Earnings',
|
||
'nav-sectors': '🏭 Sectors',
|
||
'nav-debate': '⚔️ Bull/Bear',
|
||
'nav-calendar': '📅 Calendar',
|
||
'nav-my': '⭐ My Dashboard',
|
||
}
|
||
};
|
||
|
||
// Section label maps (by section id + .section-label text)
|
||
var SECTION_LABELS = {
|
||
zh: {
|
||
'sec-news': '📰 市场要闻 · Market News',
|
||
'sec-ai-trading': '🤖 AI 操盘实验 · AI Trading Challenge',
|
||
'sec-watchlist': '🎯 AI 决策仪表盘 · AI Watchlist',
|
||
'sec-macro': '📈 宏观指标 · Macro',
|
||
'sec-debate': '⚔️ 多空辩论 · Bull vs Bear',
|
||
'sec-earnings': '💼 财报追踪 · Earnings',
|
||
'sec-sectors': '🏭 板块表现 · Sectors',
|
||
'sec-replay': '🎥 大盘复盘 · Replay',
|
||
'sec-calendar': '📅 本周日程 · Calendar',
|
||
'sec-leaders': '👔 领袖观点 · Leaders',
|
||
'sec-banks': '🏦 幂行动态 · Central Banks',
|
||
},
|
||
en: {
|
||
'sec-news': '📰 Market News',
|
||
'sec-ai-trading': '🤖 AI Trading Challenge',
|
||
'sec-watchlist': '🎯 AI Watchlist',
|
||
'sec-macro': '📈 Macro Indicators',
|
||
'sec-debate': '⚔️ Bull vs Bear',
|
||
'sec-earnings': '💼 Earnings Tracker',
|
||
'sec-sectors': '🏭 Sector Performance',
|
||
'sec-replay': '🎥 Market Replay',
|
||
'sec-calendar': '📅 Economic Calendar',
|
||
'sec-leaders': '👔 Market Leaders',
|
||
'sec-banks': '🏦 Central Banks',
|
||
}
|
||
};
|
||
|
||
// Static UI strings (selectors → text)
|
||
var STATIC_EN = [
|
||
['.site-title', 'Financial Daily'],
|
||
['.site-sub', 'AI-Powered · Daily Briefing'],
|
||
['.subscribe-strip-body strong', 'Subscribe to Daily Briefing'],
|
||
['.subscribe-strip-body span', 'Updated twice daily, every trading day'],
|
||
['.btn-sub-email', '📧 Email'],
|
||
['#pod-play', '▶ Play'],
|
||
['.podcast-title', 'Financial Daily · Today\'s Podcast'],
|
||
['.my-drawer-toggle span:first-child', '⭐ My Dashboard'],
|
||
['#ptab-wl', '📋 Watchlist'],
|
||
['#ptab-pf', '💼 Portfolio P&L'],
|
||
['#ptab-sim', '📊 Simulator'],
|
||
['#wl-empty', 'No stocks yet. Enter a symbol to start tracking →'],
|
||
['#pf-empty', 'No positions yet. Add holdings to track P&L →'],
|
||
['.footer-inner span:nth-child(2)', 'AI-generated content for informational purposes only. Not investment advice.'],
|
||
];
|
||
var STATIC_ZH = [
|
||
['.site-title', '金融日报'],
|
||
['.site-sub', 'Financial Daily'],
|
||
['.subscribe-strip-body strong', '订阅每日金融早报'],
|
||
['.subscribe-strip-body span', '每个交易日自动更新,开盘前 + 收盘后各一次'],
|
||
['.btn-sub-email', '📧 邮件'],
|
||
['#pod-play', '▶ 播放'],
|
||
['.podcast-title', '金融日报早报 · 今日播客'],
|
||
['.my-drawer-toggle span:first-child', '⭐ 我的工作台 · My Dashboard'],
|
||
['#ptab-wl', '📋 自选股'],
|
||
['#ptab-pf', '💼 持仓盈亏'],
|
||
['#ptab-sim', '📊 模拟器'],
|
||
['#wl-empty', '还没有自选股,输入代码开始追踪 →'],
|
||
['#pf-empty', '还没有持仓记录,输入持仓开始计算浮盈浮亏 →'],
|
||
['.footer-inner span:nth-child(2)', '内容由AI自动生成,仅供参考,不构成投资建议'],
|
||
];
|
||
|
||
function applyLang(lang) {
|
||
// Toggle body class
|
||
document.body.classList.remove('lang-zh', 'lang-en');
|
||
document.body.classList.add('lang-' + lang);
|
||
|
||
// Update toggle button active state
|
||
var elZH = document.getElementById('lang-zh');
|
||
var elEN = document.getElementById('lang-en');
|
||
if (elZH) elZH.classList.toggle('active', lang === 'zh');
|
||
if (elEN) elEN.classList.toggle('active', lang === 'en');
|
||
|
||
// data-i18n nav items
|
||
var map = LANG[lang];
|
||
document.querySelectorAll('[data-i18n]').forEach(function(el) {
|
||
var key = el.getAttribute('data-i18n');
|
||
if (map[key]) el.textContent = map[key];
|
||
});
|
||
|
||
// Section labels
|
||
var slMap = SECTION_LABELS[lang];
|
||
Object.entries(slMap).forEach(function(entry) {
|
||
var secId = entry[0], text = entry[1];
|
||
var sec = document.getElementById(secId);
|
||
if (!sec) return;
|
||
var lbl = sec.querySelector('.section-label');
|
||
if (lbl) lbl.childNodes.forEach(function(n){ if (n.nodeType === 3 && n.textContent.trim()) n.textContent = ' ' + text; });
|
||
});
|
||
|
||
// Static UI strings
|
||
var statics = lang === 'en' ? STATIC_EN : STATIC_ZH;
|
||
statics.forEach(function(pair) {
|
||
try {
|
||
var el = document.querySelector(pair[0]);
|
||
if (el) el.textContent = pair[1];
|
||
} catch(e) {}
|
||
});
|
||
|
||
// date format
|
||
var dateEl = document.getElementById('current-date');
|
||
if (dateEl) {
|
||
var now = new Date();
|
||
var opts = lang === 'en'
|
||
? { year: 'numeric', month: 'short', day: 'numeric', weekday: 'short' }
|
||
: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'short' };
|
||
dateEl.textContent = now.toLocaleDateString(lang === 'en' ? 'en-US' : 'zh-CN', opts);
|
||
}
|
||
|
||
// AI portfolio section heading (dynamic)
|
||
var lbHdr = document.querySelector('#ai-portfolio-content > div:first-child');
|
||
if (lbHdr) lbHdr.innerHTML = lang === 'en'
|
||
? '🏆 Leaderboard <span style="font-size:.68rem;color:var(--dimmed);margin-left:6px;">Starting capital $10,000 · Baseline: SPY buy & hold</span>'
|
||
: '🏆 排行榜 · Leaderboard <span style="font-size:.68rem;color:var(--dimmed);margin-left:6px;">初始资金 $10,000 · 基线 SPY买入持有</span>';
|
||
|
||
localStorage.setItem('fd-lang', lang);
|
||
|
||
// ── Google Translate for dynamic content ───────────────────
|
||
triggerGoogleTranslate(lang);
|
||
}
|
||
|
||
// ── Google Translate helper ─────────────────────────────────
|
||
var _gtRetries = 0;
|
||
function triggerGoogleTranslate(lang) {
|
||
var targetLang = lang === 'en' ? 'en' : 'zh-CN';
|
||
|
||
// Method 1: drive the hidden <select> that GT injects
|
||
function driveSelect() {
|
||
var sel = document.querySelector('.goog-te-combo');
|
||
if (sel) {
|
||
sel.value = targetLang;
|
||
sel.dispatchEvent(new Event('change', { bubbles: true }));
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// Method 2: cookie + reload fallback
|
||
function cookieFallback() {
|
||
var exp = new Date();
|
||
exp.setTime(exp.getTime() + 30 * 24 * 60 * 60 * 1000);
|
||
var expStr = '; expires=' + exp.toUTCString() + '; path=/';
|
||
if (lang === 'en') {
|
||
document.cookie = 'googtrans=/zh-CN/en' + expStr;
|
||
document.cookie = 'googtrans=/zh-CN/en' + expStr + '; domain=.' + window.location.hostname;
|
||
} else {
|
||
document.cookie = 'googtrans=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC';
|
||
document.cookie = 'googtrans=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.' + window.location.hostname;
|
||
}
|
||
// Small delay then reload so cookie takes effect
|
||
setTimeout(function() { window.location.reload(); }, 150);
|
||
}
|
||
|
||
// Try select-drive first; retry up to 8 times while GT loads
|
||
if (driveSelect()) { _gtRetries = 0; return; }
|
||
if (_gtRetries < 8) {
|
||
_gtRetries++;
|
||
setTimeout(function() { triggerGoogleTranslate(lang); }, 400);
|
||
} else {
|
||
// GT never loaded (e.g. blocked) — use cookie+reload
|
||
_gtRetries = 0;
|
||
cookieFallback();
|
||
}
|
||
}
|
||
|
||
window.toggleLang = function() {
|
||
var current = localStorage.getItem('fd-lang') || 'zh';
|
||
applyLang(current === 'zh' ? 'en' : 'zh');
|
||
};
|
||
|
||
// Apply saved lang on load (after DOM ready)
|
||
var savedLang = localStorage.getItem('fd-lang') || 'zh';
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
applyLang(savedLang);
|
||
});
|
||
|
||
// Also expose for external call after dynamic content loads
|
||
window.applyCurrentLang = function() {
|
||
applyLang(localStorage.getItem('fd-lang') || 'zh');
|
||
};
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html> |