3318 lines
165 KiB
HTML
3318 lines
165 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 flat">
|
||
<div class="ticker-name">S&P 500</div>
|
||
<div class="ticker-value">7,483.24</div>
|
||
<div class="ticker-change flat">– +0.00%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">道琼斯工业平均指数</div>
|
||
<div class="ticker-value">52,900.07</div>
|
||
<div class="ticker-change up">▲ +1.14%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">纳斯达克综合指数</div>
|
||
<div class="ticker-value">25,832.67</div>
|
||
<div class="ticker-change down">▼ -0.80%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">上证综指</div>
|
||
<div class="ticker-value">4,044.00</div>
|
||
<div class="ticker-change up">▲ +0.37%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">恒生指数</div>
|
||
<div class="ticker-value">23,055.03</div>
|
||
<div class="ticker-change up">▲ +0.76%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">日经225</div>
|
||
<div class="ticker-value">69,744.07</div>
|
||
<div class="ticker-change up">▲ +1.47%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">德国DAX</div>
|
||
<div class="ticker-value">25,779.31</div>
|
||
<div class="ticker-change up">▲ +0.78%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">黄金现货</div>
|
||
<div class="ticker-value">4,187.30</div>
|
||
<div class="ticker-change up">▲ +1.49%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">WTI原油</div>
|
||
<div class="ticker-value">68.78</div>
|
||
<div class="ticker-change up">▲ +0.13%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">比特币</div>
|
||
<div class="ticker-value">61,677.96</div>
|
||
<div class="ticker-change up">▲ +2.06%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">美元指数</div>
|
||
<div class="ticker-value">105.20</div>
|
||
<div class="ticker-change down">▼ -0.28%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">离岸人民币</div>
|
||
<div class="ticker-value">6.7836</div>
|
||
<div class="ticker-change down">▼ -0.06%</div>
|
||
</div>
|
||
<!-- INDICES:END -->
|
||
<!-- INDICES:END -->
|
||
<!-- duplicate for seamless loop -->
|
||
<!-- INDICES:START -->
|
||
<div class="ticker-card flat">
|
||
<div class="ticker-name">S&P 500</div>
|
||
<div class="ticker-value">7,483.24</div>
|
||
<div class="ticker-change flat">– +0.00%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">道琼斯工业平均指数</div>
|
||
<div class="ticker-value">52,900.07</div>
|
||
<div class="ticker-change up">▲ +1.14%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">纳斯达克综合指数</div>
|
||
<div class="ticker-value">25,832.67</div>
|
||
<div class="ticker-change down">▼ -0.80%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">上证综指</div>
|
||
<div class="ticker-value">4,044.00</div>
|
||
<div class="ticker-change up">▲ +0.37%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">恒生指数</div>
|
||
<div class="ticker-value">23,055.03</div>
|
||
<div class="ticker-change up">▲ +0.76%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">日经225</div>
|
||
<div class="ticker-value">69,744.07</div>
|
||
<div class="ticker-change up">▲ +1.47%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">德国DAX</div>
|
||
<div class="ticker-value">25,779.31</div>
|
||
<div class="ticker-change up">▲ +0.78%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">黄金现货</div>
|
||
<div class="ticker-value">4,187.30</div>
|
||
<div class="ticker-change up">▲ +1.49%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">WTI原油</div>
|
||
<div class="ticker-value">68.78</div>
|
||
<div class="ticker-change up">▲ +0.13%</div>
|
||
</div>
|
||
<div class="ticker-card up">
|
||
<div class="ticker-name">比特币</div>
|
||
<div class="ticker-value">61,677.96</div>
|
||
<div class="ticker-change up">▲ +2.06%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">美元指数</div>
|
||
<div class="ticker-value">105.20</div>
|
||
<div class="ticker-change down">▼ -0.28%</div>
|
||
</div>
|
||
<div class="ticker-card down">
|
||
<div class="ticker-name">离岸人民币</div>
|
||
<div class="ticker-value">6.7836</div>
|
||
<div class="ticker-change down">▼ -0.06%</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.81</span></span>
|
||
</div>
|
||
<div class="summary-headline">美国独立日休市,就业数据疲软引发降息预期升温</div>
|
||
<ul class="key-points"><li class="kp-item"><span class="kp-arrow">▸</span>6月非农仅增5.7万大幅不及预期</li><li class="kp-item"><span class="kp-arrow">▸</span>芯片股延续抛售纳指跌0.8%</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">7月2日公布的6月非农就业仅增5.7万,远低于预期的11.3万,失业率4.2%。市场押注美联储9月加息概率从67%降至50%以下。道指收涨1.14%创新高,但纳指因半导体抛售跌0.8%,科技股延续获利回吐。7月3-4日美股休市,投资者需关注下周就业数据对降息预期的进一步影响。</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">6月非农就业大幅不及预期,降息预期升温</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>就业数据疲软利好黄金和债券,科技股投资者可关注降息预期带来的流动性改善机会。</div>
|
||
<div class="news-body-collapse">美国6月新增非农就业仅5.7万人,远低于预期的11.3万,为四个月来最低。失业率为4.2%,略好于预期的4.3%。休闲和酒店业减少6.1万就业岗位。这一疲软数据打破了三个月的强劲势头,市场对美联储9月加息的押注从67%降至50%以下,黄金因降息预期升温涨1.49%。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge breaking">🔥 BREAKING</span>
|
||
<div class="tags"><span class="tag">#就业数据</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">半导体板块延续抛售,纳指跌0.8%</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>半导体获利回吐属于健康调整,长期AI趋势未变,可等待技术面支撑位再入场。</div>
|
||
<div class="news-body-collapse">7月2日芯片股延续周三的大幅下跌,Micron暴跌超10%,AMD、Intel、Marvell均下跌9-10%。费城半导体指数跌6.7%,此前第二季度涨幅达100%。投资者担忧AI估值过度,开始从科技股获利了结转向道指蓝筹股。特斯拉虽交付量超预期仍跌7%。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge breaking">🔥 BREAKING</span>
|
||
<div class="tags"><span class="tag">#半导体</span><span class="tag">#科技股</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="news-item high featured" 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>关税谈判有进展利好中美贸易股和农业板块,但强迫劳动调查仍是潜在风险点。</div>
|
||
<div class="news-body-collapse">7月2日彭博报道,中美两国正寻求削减部分农产品关税,以维护去年达成的更广泛贸易休战协议。中国商务部表示双方原则同意将农产品纳入互惠关税削减框架。此前美国贸易代表办公室6月2日提议对中国加征12.5%强迫劳动相关关税,评议期至7月6日。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge high">• HIGH</span>
|
||
<div class="tags"><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">Meta宣布推出云计算业务出售过剩算力</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>Meta进军云计算显示大型科技公司变现AI投资的新路径,对专业AI云厂商构成竞争压力。</div>
|
||
<div class="news-body-collapse">Meta股价7月1日大涨近9%,此前公司宣布将推出云计算业务,出售过剩计算能力。这一战略转变可能为Meta带来新收入来源。与此同时,Nebius暴跌17%,CoreWeave跌14%,成为纳斯达克当日最大输家。AI基础设施竞争格局正在重塑。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge high">• HIGH</span>
|
||
<div class="tags"><span class="tag">#Meta</span><span class="tag">#云计算</span><span class="tag">#AI</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>北美贸易不确定性上升,持有墨西哥资产和跨国供应链企业需警惕政策风险。</div>
|
||
<div class="news-body-collapse">7月1日美国决定不续签与墨西哥和加拿大的区域贸易协议,启动审查程序。此举为USMCA协议带来不确定性。特朗普政府正通过多个Section 301和Section 232调查重建关税体系,以替代最高法院裁定非法的IEEPA关税。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge high">• HIGH</span>
|
||
<div class="tags"><span class="tag">#USMCA</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">日元跌至40年低点引发干预担忧</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>日元剧烈波动可能触发全球市场连锁反应,多元化配置和对冲策略变得更加重要。</div>
|
||
<div class="news-body-collapse">美元兑日元本周触及40年低点,引发市场对日本政府干预和波动性的担忧。两年前类似水平时,日元单日大涨近3%,四周内涨近9%。同期日经225跌25%,标普500跌约8%。拥挤的日元套利交易和日元空头头寸可能引发市场动荡。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge high">• HIGH</span>
|
||
<div class="tags"><span class="tag">#外汇</span><span class="tag">#日元</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="news-item medium" onclick="toggleNews(this)">
|
||
<div class="news-header">
|
||
<span class="imp-dot medium"></span>
|
||
<div class="news-title">Russell 2000小盘股第二季度暴涨22%</div>
|
||
<span class="news-expand-icon">+</span>
|
||
</div>
|
||
<div class="news-tldr"><span class="tldr-label">💡 新手解读</span>小盘股强势反映市场风格扩散,适合分散配置降低大盘科技股集中度风险。</div>
|
||
<div class="news-body-collapse">Russell 2000第二季度上涨22%,创1991年以来最佳上半年表现。小盘股从科技股轮动中受益,显示市场广度扩大。7月2日小盘股指数跌1.08%至2,980.05,但整体趋势向好。</div>
|
||
<div class="news-footer">
|
||
<span class="importance-badge medium">• MEDIUM</span>
|
||
<div class="tags"><span class="tag">#小盘股</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">194.44</div><div class="wc-price-chg down">-2.4%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>62</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">短期获利回吐健康,长期AI趋势不变</div>
|
||
<div class="wc-driver">半导体板块整体抛售,投资者担忧AI估值过度,但Q2营收指引910亿美元显示需求强劲</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 220</span><span class="wc-target-down">↓ 180</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">持有观望,等待200日均线支撑192美元附近企稳后可加仓,止损设180</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#AI</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">497.20</div><div class="wc-price-chg down">-1.61%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>58</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">科技股调整属于健康获利了结</div>
|
||
<div class="wc-driver">芯片股大跌拖累,但降息预期升温中长期利好成长股,关注50日均线490支撑</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 520</span><span class="wc-target-down">↓ 480</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">490附近可分批建仓,破485止损,目标520</div>
|
||
<div class="tags" style="margin-top:8px"><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">551.80</div><div class="wc-price-chg ">+0.00%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge BUY">BUY</span>
|
||
<span class="wc-score">评分 <span>72</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">大盘韧性强,轮动有利广度改善</div>
|
||
<div class="wc-driver">就业数据疲软支持降息预期,道指创新高显示市场广度扩大,防御板块轮动健康</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 575</span><span class="wc-target-down">↓ 535</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">回调至545-548区间可加仓,长期持有,止损设535</div>
|
||
<div class="tags" style="margin-top:8px"><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">61,677.96</div><div class="wc-price-chg up">+2.06%</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">美元走弱和流动性预期改善支撑,但未能突破65,000压力位,与纳指相关性高达0.72</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 68,000</span><span class="wc-target-down">↓ 58,000</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">不建议追高,等待回踩58,000-60,000支撑区再入场</div>
|
||
<div class="tags" style="margin-top:8px"><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">3.92</div><div class="wc-price-chg up">+0.5%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge BUY">BUY</span>
|
||
<span class="wc-score">评分 <span>68</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">PMI数据企稳,估值洼地显现</div>
|
||
<div class="wc-driver">制造业PMI升至50.3超预期,AI股反弹,人民币贬值压力可控,外资流入迹象</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 4.20</span><span class="wc-target-down">↓ 3.70</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">3.85-3.90可建仓,目标4.20,止损3.70</div>
|
||
<div class="tags" style="margin-top:8px"><span class="tag">#A股</span><span class="tag">#中国ETF</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,187.30</div><div class="wc-price-chg up">+1.49%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge BUY">BUY</span>
|
||
<span class="wc-score">评分 <span>78</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">降息预期升温,避险需求强</div>
|
||
<div class="wc-driver">就业数据疲软推动降息预期,美元走弱,地缘风险(伊朗-美国)支撑避险需求</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 4,350</span><span class="wc-target-down">↓ 4,050</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">4,100-4,150回调可加仓,长期看涨至4,350,止损4,000</div>
|
||
<div class="tags" style="margin-top:8px"><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">68.78</div><div class="wc-price-chg up">+0.13%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>52</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">区间震荡,霍尔木兹海峡恢复限制上涨</div>
|
||
<div class="wc-driver">伊朗冲突缓和,霍尔木兹海峡通航恢复,需求端因就业疲软承压,67-76美元区间</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 76</span><span class="wc-target-down">↓ 64</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">区间交易为主,破64做空,突破72做多</div>
|
||
<div class="tags" style="margin-top:8px"><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">384.50</div><div class="wc-price-chg down">-7.49%</div></div>
|
||
</div>
|
||
<div class="wc-signal-row">
|
||
<span class="signal-badge HOLD">HOLD</span>
|
||
<span class="wc-score">评分 <span>48</span>/100</span>
|
||
</div>
|
||
<div class="wc-conclusion">交付量超预期仍遭抛售,观望7/22财报</div>
|
||
<div class="wc-driver">Q2交付量超预期但股价跌7%,Semi首起致命事故打击情绪,7月底Optimus 3发布和7/22财报是关键</div>
|
||
<div class="wc-targets"><span class="wc-target-up">↑ 440</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可轻仓试多,止损340</div>
|
||
<div class="tags" style="margin-top:8px"><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-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> 前值: 129,000</div>
|
||
<div class="macro-desc">6月新增就业远低于预期的113,000,为四个月最低,支持美联储维持观望立场</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%,但劳动参与率下降导致失业率降低</div>
|
||
</div>
|
||
<div class="macro-card dir-down">
|
||
<div class="macro-indicator">Initial Jobless Claims</div>
|
||
<div class="macro-value macro-value-down">215,000</div>
|
||
<div class="macro-prev"><span class="arrow down">↓</span> 前值: 219,000</div>
|
||
<div class="macro-desc">初请失业金人数低于预期,显示劳动力市场韧性尚存</div>
|
||
</div>
|
||
<div class="macro-card dir-up">
|
||
<div class="macro-indicator">Manufacturing PMI (Official)</div>
|
||
<div class="macro-value macro-value-up">50.3</div>
|
||
<div class="macro-prev"><span class="arrow up">↑</span> 前值: 50.0</div>
|
||
<div class="macro-desc">中国官方制造业PMI升至50.3,高于预期的50.1,显示经济二季度末稳固</div>
|
||
</div>
|
||
<div class="macro-card dir-down">
|
||
<div class="macro-indicator">Manufacturing PMI (Caixin)</div>
|
||
<div class="macro-value macro-value-down">51.7</div>
|
||
<div class="macro-prev"><span class="arrow down">↓</span> 前值: 51.8</div>
|
||
<div class="macro-desc">中国财新制造业PMI微降至51.7,但仍在扩张区间</div>
|
||
</div>
|
||
<div class="macro-card dir-up">
|
||
<div class="macro-indicator">Non-Manufacturing PMI</div>
|
||
<div class="macro-value macro-value-up">50.2</div>
|
||
<div class="macro-prev"><span class="arrow up">↑</span> 前值: 50.1</div>
|
||
<div class="macro-desc">中国非制造业PMI升至50.2,超预期的49.9,服务业持续企稳</div>
|
||
</div>
|
||
<div class="macro-card dir-down">
|
||
<div class="macro-indicator">Services PMI (Caixin)</div>
|
||
<div class="macro-value macro-value-down">54.1</div>
|
||
<div class="macro-prev"><span class="arrow down">↓</span> 前值: 54.4</div>
|
||
<div class="macro-desc">中国财新服务业PMI降至54.1,仍高于预期的53,保持强劲增长</div>
|
||
</div>
|
||
<div class="macro-card dir-up">
|
||
<div class="macro-indicator">Average Hourly Earnings YoY</div>
|
||
<div class="macro-value macro-value-up">3.5%</div>
|
||
<div class="macro-prev"><span class="arrow up">↑</span> 前值: 3.4%</div>
|
||
<div class="macro-desc">工资增长边际加速至3.5%,可能限制美联储降息空间</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 本周展望(7/6-7/10)</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>就业数据疲软强化降息预期,流动性改善利好估值</div>
|
||
<div class="debate-point"><span class="conf-badge high">high</span>市场轮动至道指蓝筹和防御板块,广度改善支撑大盘</div>
|
||
<div class="debate-point"><span class="conf-badge medium">medium</span>7/10 CPI预期降至3.1%,通胀降温为降息铺路</div>
|
||
</div>
|
||
<div class="debate-side bear">
|
||
<div class="debate-side-title">🐻 空方看跌</div>
|
||
<div class="debate-point"><span class="conf-badge high">high</span>半导体持续抛售可能蔓延至大型科技股,拖累指数</div>
|
||
<div class="debate-point"><span class="conf-badge high">high</span>就业疲软预示经济放缓,企业盈利增长面临下行风险</div>
|
||
<div class="debate-point"><span class="conf-badge medium">medium</span>7/7关税听证会可能引发贸易摩擦担忧,压制风险偏好</div>
|
||
</div>
|
||
</div>
|
||
<div class="debate-verdict">
|
||
<div class="verdict-label">裁判结论 <span class="verdict-lean bullish">BULLISH</span></div>
|
||
<div class="verdict-text">标普500本周中性偏多,降息预期和市场轮动提供支撑,但需警惕科技股抛售扩散。建议关注7/10 CPI数据,若低于预期可确认多头趋势。操作上持有SPY,选择性在545-548回调加仓。</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>N/A</span></div>
|
||
<div class="earnings-highlight">7月2日公布Q2交付量超预期,但股价仍跌7%,市场关注7月22日财报</div>
|
||
</div>
|
||
<div class="earnings-card pending">
|
||
<div class="earnings-ticker">NVDA</div>
|
||
<div class="earnings-company">Nvidia</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-08-26</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>预期Q2营收910亿美元</span></div>
|
||
<div class="earnings-highlight">股价在192-200美元波动,AI需求持续但估值担忧加剧</div>
|
||
</div>
|
||
<div class="earnings-card pending">
|
||
<div class="earnings-ticker">META</div>
|
||
<div class="earnings-company">Meta Platforms</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-24</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>N/A</span></div>
|
||
<div class="earnings-highlight">7月1日宣布推出云计算业务,股价大涨9%</div>
|
||
</div>
|
||
<div class="earnings-card pending">
|
||
<div class="earnings-ticker">RIVN</div>
|
||
<div class="earnings-company">Rivian</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-30</div>
|
||
</div>
|
||
</div>
|
||
<div class="earnings-revenue">收入: <span>N/A</span></div>
|
||
<div class="earnings-highlight">上调2026年交付指引,电动车需求强劲,股价早盘涨近5%</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 down" style="width:71%"></div></div>
|
||
<div class="sector-pct down">-1.67%</div>
|
||
</div>
|
||
<div class="sector-row">
|
||
<div><div class="sector-name">金融</div><div class="sector-name-en">Financial Services</div></div>
|
||
<div class="sector-etf">XLF</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:40%"></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">Industrials</div></div>
|
||
<div class="sector-etf">XLI</div>
|
||
<div class="sector-bar-wrap"><div class="sector-bar up" style="width:46%"></div></div>
|
||
<div class="sector-pct up">+0.98%</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 down" style="width:41%"></div></div>
|
||
<div class="sector-pct down">-0.85%</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 down" style="width:48%"></div></div>
|
||
<div class="sector-pct down">-1.03%</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 up" style="width:99%"></div></div>
|
||
<div class="sector-pct up">+2.42%</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:100%"></div></div>
|
||
<div class="sector-pct up">+2.43%</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 up" style="width:48%"></div></div>
|
||
<div class="sector-pct up">+1.05%</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:74%"></div></div>
|
||
<div class="sector-pct up">+1.73%</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 up" style="width:56%"></div></div>
|
||
<div class="sector-pct up">+1.26%</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:77%"></div></div>
|
||
<div class="sector-pct up">+1.83%</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">道指创新高涨1.14%,纳指半导体抛售跌0.8%,市场轮动明显</div>
|
||
<div class="ad-bar-wrap">
|
||
<span class="ad-label-up">↑3240</span>
|
||
<div class="ad-bar adv" style="width:64px;max-width:80px"></div>
|
||
<div class="ad-bar dec" style="width:36px;max-width:80px"></div>
|
||
<span class="ad-label-dn">↓1820</span>
|
||
</div>
|
||
<div class="replay-stats">
|
||
<div class="replay-stat"><div class="replay-stat-label">热门板块</div><div class="replay-stat-val">必需消费+2.43%</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">领涨标的</div><div class="replay-stat-val">Meta +8.9%(宣布云计算业务)</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">创52周新高</div><div class="replay-stat-val">32</div></div>
|
||
</div>
|
||
</div><div class="replay-card">
|
||
<div class="replay-card-title">🇨🇳 A股</div>
|
||
<div class="replay-summary">上证涨0.37%至4,044,PMI超预期支撑科技股反弹</div>
|
||
<div class="ad-bar-wrap">
|
||
<span class="ad-label-up">↑2800</span>
|
||
<div class="ad-bar adv" style="width:65px;max-width:80px"></div>
|
||
<div class="ad-bar dec" style="width:35px;max-width:80px"></div>
|
||
<span class="ad-label-dn">↓1500</span>
|
||
</div>
|
||
<div class="replay-stats">
|
||
<div class="replay-stat"><div class="replay-stat-label">热门板块</div><div class="replay-stat-val">AI算力、新能源</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">领涨标的</div><div class="replay-stat-val">紫金矿业 +6.01%(贵金属受益)</div></div>
|
||
<div class="replay-stat"><div class="replay-stat-label">北向资金</div><div class="replay-stat-val">+28亿(北向资金)</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/6<br><span style="font-size:0.6rem;color:var(--dimmed)">全天</span></div>
|
||
<div>
|
||
<div class="cal-event">美股开市,独立日假期后恢复交易</div>
|
||
<div class="cal-values">预期: <span>N/A</span> · 前值: N/A</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周二 7/7<br><span style="font-size:0.6rem;color:var(--dimmed)">10:00 AM</span></div>
|
||
<div>
|
||
<div class="cal-event">美国USTR强迫劳动关税听证会</div>
|
||
<div class="cal-values">预期: <span>关注中美欧贸易摩擦</span> · 前值: 提议对60国加征10-12.5%关税</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item medium">
|
||
<div class="cal-date">周四 7/9<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM</span></div>
|
||
<div>
|
||
<div class="cal-event">Initial Jobless Claims</div>
|
||
<div class="cal-values">预期: <span>220K</span> · 前值: 215K</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周五 7/10<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM</span></div>
|
||
<div>
|
||
<div class="cal-event">June CPI YoY</div>
|
||
<div class="cal-values">预期: <span>3.1%</span> · 前值: 3.3%</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周五 7/10<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM</span></div>
|
||
<div>
|
||
<div class="cal-event">June CPI MoM</div>
|
||
<div class="cal-values">预期: <span>-0.1%</span> · 前值: 0.0%</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item medium">
|
||
<div class="cal-date">周二 7/14<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM</span></div>
|
||
<div>
|
||
<div class="cal-event">June PPI YoY</div>
|
||
<div class="cal-values">预期: <span>2.3%</span> · 前值: 2.2%</div>
|
||
</div>
|
||
</div>
|
||
<div class="cal-item high">
|
||
<div class="cal-date">周四 7/16<br><span style="font-size:0.6rem;color:var(--dimmed)">8:30 AM</span></div>
|
||
<div>
|
||
<div class="cal-event">Retail Sales MoM</div>
|
||
<div class="cal-values">预期: <span>0.3%</span> · 前值: -0.1%</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 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">5月4日</div>
|
||
</div>
|
||
<div class="leader-quote">"股价已处于相当高的估值水平"</div>
|
||
<div class="leader-body">5月在CNBC采访中,巴菲特讨论了通胀担忧和美联储政策,其现金储备已达3440亿美元创纪录高位。这显示他认为当前市场标的估值过高,缺乏有吸引力的买入机会。这一行动与美联储主席鲍威尔关于股价估值偏高的警告不谋而合。</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月下旬</div>
|
||
</div>
|
||
<div class="leader-quote">"股票价格估值相当高"</div>
|
||
<div class="leader-body">前美联储主席鲍威尔在任期内多次警告股市估值过高,但强调金融稳定风险目前不高。新任主席Kevin Warsh本周表示通胀预期正在缓和,但美联储承诺维持价格稳定。6月就业数据疲软后,市场降息预期升温。</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 default">KW</div>
|
||
<div class="leader-meta">
|
||
<div class="leader-name">Kevin Warsh <span style="color:var(--muted);font-weight:400;font-size:0.78rem;">Kevin Warsh</span></div>
|
||
<div class="leader-role">美联储主席</div>
|
||
</div>
|
||
<div class="leader-date">7月1日</div>
|
||
</div>
|
||
<div class="leader-quote">"投资者应关注数据而非央行前瞻指引"</div>
|
||
<div class="leader-body">新任美联储主席Warsh在欧洲央行论坛上敦促华尔街依据数据而非美联储指引来判断利率走向。他指出通胀预期正在缓和,但强调美联储对恢复价格稳定的承诺不变。6月就业数据疲软后,市场对9月加息概率已降至50%以下。</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 neutral">Neutral</span>
|
||
</div>
|
||
<div class="cb-rate">5.25-5.50%</div>
|
||
<div class="cb-action">维持利率5.25-5.50%区间</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">2026-07-30</span></div>
|
||
<div class="cb-note">主席Warsh强调数据依赖,6月就业疲软后9月加息概率降至50%以下</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">3.75%</div>
|
||
<div class="cb-action">举行辛特拉论坛讨论政策</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">2026-07-18</span></div>
|
||
<div class="cb-note">拉加德与各国央行行长讨论利率路径,市场关注降息线索</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">3.45%</div>
|
||
<div class="cb-action">推出隔夜逆回购操作</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">2026-07-15</span></div>
|
||
<div class="cb-note">6月30日注入3000亿元流动性,离岸人民币设定偏弱中间价引导渐进贬值</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 neutral">Neutral</span>
|
||
</div>
|
||
<div class="cb-rate">0.10%</div>
|
||
<div class="cb-action">维持超宽松政策</div>
|
||
<div class="cb-next">下次会议: <span class="font-mono" style="color:var(--neutral)">2026-07-31</span></div>
|
||
<div class="cb-note">日元跌至40年低点,市场担忧政府干预,套利交易平仓可能引发市场剧震</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> |