1246 lines
80 KiB
HTML
1246 lines
80 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Verimund — UI Mockup</title>
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
--bg: #0a0a0a;
|
||
--bg2: #111111;
|
||
--bg3: #181818;
|
||
--border: #222222;
|
||
--fg: #ffffff;
|
||
--fg2: #888888;
|
||
--fg3: #3a3a3a;
|
||
--positive: #4ade80;
|
||
--negative: #f87171;
|
||
--warning: #fbbf24;
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
background: var(--bg); color: var(--fg);
|
||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||
font-size: 13px; height: 100vh; overflow: hidden;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
.app { display: flex; height: 100vh; }
|
||
|
||
/* ── SIDEBAR ─────────────────────────────────── */
|
||
.sidebar {
|
||
width: 72px; background: var(--bg2);
|
||
border-right: 1px solid var(--border);
|
||
display: flex; flex-direction: column; align-items: center;
|
||
padding: 16px 0 12px; flex-shrink: 0; z-index: 10;
|
||
}
|
||
.sidebar-logo {
|
||
width: 36px; height: 36px; background: var(--fg); color: var(--bg);
|
||
border-radius: 8px; display: flex; align-items: center; justify-content: center;
|
||
font-weight: 900; font-size: 15px; margin-bottom: 28px;
|
||
letter-spacing: -0.03em; user-select: none;
|
||
}
|
||
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; width: 100%; padding: 0 8px; flex: 1; }
|
||
.nav-item {
|
||
display: flex; flex-direction: column; align-items: center; gap: 5px;
|
||
padding: 9px 0; border-radius: 8px; cursor: pointer; color: var(--fg3);
|
||
transition: color 0.15s; position: relative; user-select: none;
|
||
}
|
||
.nav-item:hover { color: var(--fg2); }
|
||
.nav-item.active { color: var(--fg); }
|
||
.nav-item.active::before {
|
||
content: ''; position: absolute; left: -8px; top: 50%; transform: translateY(-50%);
|
||
width: 3px; height: 22px; background: var(--fg); border-radius: 0 2px 2px 0;
|
||
}
|
||
.nav-icon { font-size: 17px; line-height: 1; }
|
||
.nav-label { font-size: 10px; font-weight: 500; letter-spacing: 0.02em; }
|
||
.sidebar-bottom { padding: 0 8px; width: 100%; }
|
||
|
||
/* ── MAIN ──────────────────────────────────────── */
|
||
.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
|
||
.page { display: none; flex: 1; flex-direction: column; overflow: hidden; min-height: 0; }
|
||
.page.active { display: flex; }
|
||
|
||
/* ── TOOLBAR ───────────────────────────────────── */
|
||
.toolbar {
|
||
padding: 10px 16px; border-bottom: 1px solid var(--border);
|
||
background: var(--bg2); flex-shrink: 0;
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.toolbar-row { display: contents; }
|
||
|
||
/* Pill groups */
|
||
.pill-group {
|
||
display: inline-flex; align-items: stretch;
|
||
border: 1px solid var(--border); border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
.pill-btn {
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 6px 14px; font-size: 12px; font-weight: 600;
|
||
cursor: pointer; border: none; background: transparent;
|
||
color: var(--fg2); white-space: nowrap; font-family: inherit;
|
||
border-right: 1px solid var(--border); transition: background 0.12s, color 0.12s;
|
||
}
|
||
.pill-btn:last-child { border-right: none; }
|
||
.pill-btn:hover { background: rgba(255,255,255,0.04); color: var(--fg); }
|
||
.pill-btn.run {
|
||
background: var(--fg); color: var(--bg); font-weight: 700; letter-spacing: 0.01em;
|
||
}
|
||
.pill-btn.run:hover { opacity: 0.88; background: var(--fg); }
|
||
.pill-btn.stop { color: var(--fg3); cursor: not-allowed; opacity: 0.4; }
|
||
|
||
select.pill-sel {
|
||
-webkit-appearance: none; appearance: none;
|
||
background: transparent;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%23555'/%3E%3C/svg%3E");
|
||
background-repeat: no-repeat; background-position: right 9px center;
|
||
color: var(--fg2); border: none;
|
||
border-right: 1px solid var(--border);
|
||
padding: 6px 26px 6px 12px; font-size: 12px;
|
||
cursor: pointer; outline: none; font-family: inherit;
|
||
min-width: 80px; transition: color 0.12s;
|
||
}
|
||
select.pill-sel:last-child { border-right: none; }
|
||
select.pill-sel:hover { color: var(--fg); }
|
||
select.pill-sel option { background: #1a1a1a; color: var(--fg); }
|
||
|
||
.pill-solo {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
padding: 6px 13px; border: 1px solid var(--border); border-radius: 8px;
|
||
font-size: 12px; font-weight: 500; color: var(--fg2); cursor: pointer;
|
||
white-space: nowrap; transition: color 0.12s, border-color 0.12s;
|
||
}
|
||
.pill-solo:hover { color: var(--fg); border-color: #444; }
|
||
|
||
.toolbar-spacer { flex: 1; }
|
||
.toolbar-ts { font-size: 11px; color: var(--fg3); white-space: nowrap; margin-left: auto; }
|
||
|
||
/* ── PROGRESS ──────────────────────────────────── */
|
||
.progress-wrap { height: 2px; background: var(--border); flex-shrink: 0; }
|
||
.progress-fill { height: 100%; background: var(--fg); }
|
||
|
||
/* ── CONTENT SPLIT ─────────────────────────────── */
|
||
.content-split { display: flex; flex: 1; overflow: hidden; min-height: 0; }
|
||
|
||
/* ── STOCK LIST ────────────────────────────────── */
|
||
.stock-list {
|
||
width: 310px; flex-shrink: 0; border-right: 1px solid var(--border);
|
||
display: flex; flex-direction: column; overflow: hidden;
|
||
}
|
||
.list-header {
|
||
padding: 10px 16px 8px; display: flex; align-items: center; justify-content: space-between;
|
||
flex-shrink: 0; border-bottom: 1px solid var(--border);
|
||
}
|
||
.list-header-title { font-size: 10px; font-weight: 700; color: var(--fg2); text-transform: uppercase; letter-spacing: 0.08em; }
|
||
.list-header-count { font-size: 11px; color: var(--fg3); }
|
||
.stock-table-scroll { flex: 1; overflow-y: auto; }
|
||
.stock-table-scroll::-webkit-scrollbar { width: 4px; }
|
||
.stock-table-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||
table.stocks { width: 100%; border-collapse: collapse; }
|
||
table.stocks thead th {
|
||
padding: 6px 14px; text-align: left; font-size: 10px; font-weight: 700;
|
||
color: var(--fg3); text-transform: uppercase; letter-spacing: 0.06em;
|
||
border-bottom: 1px solid var(--border); background: var(--bg); position: sticky; top: 0; z-index: 1;
|
||
}
|
||
table.stocks thead th.r { text-align: right; }
|
||
table.stocks tbody tr { cursor: pointer; transition: background 0.1s; }
|
||
table.stocks tbody tr:nth-child(even) { background: rgba(255,255,255,0.02); }
|
||
table.stocks tbody tr:hover { background: #1c1c1c; }
|
||
table.stocks tbody tr.selected { background: #1a1a1a; }
|
||
table.stocks tbody tr.selected td:first-child { border-left: 2px solid var(--fg); padding-left: 12px; }
|
||
table.stocks tbody td { padding: 8px 14px; font-size: 12px; vertical-align: middle; }
|
||
table.stocks tbody td.num { color: var(--fg3); font-size: 11px; width: 28px; }
|
||
table.stocks tbody td.ticker { font-weight: 700; font-size: 13px; width: 52px; }
|
||
table.stocks tbody td.name { color: var(--fg2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 120px; }
|
||
table.stocks tbody td.score-cell { text-align: right; width: 36px; font-weight: 700; font-size: 13px; }
|
||
.score-hi { color: var(--positive); }
|
||
.score-mid { color: var(--fg); }
|
||
.score-lo { color: var(--fg2); }
|
||
|
||
/* ── DETAIL PANEL ──────────────────────────────── */
|
||
.detail-panel { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
|
||
.detail-empty { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--fg3); font-size: 14px; }
|
||
.detail-filled { display: flex; flex-direction: column; flex: 1; overflow: hidden; }
|
||
.detail-header { padding: 14px 20px 0; flex-shrink: 0; }
|
||
.detail-ticker-row { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
|
||
.detail-ticker { font-size: 22px; font-weight: 800; letter-spacing: -0.02em; }
|
||
.detail-name { font-size: 14px; color: var(--fg2); }
|
||
.detail-sector { font-size: 12px; color: var(--fg3); }
|
||
.detail-tabs {
|
||
display: flex; padding: 0 20px; border-bottom: 1px solid var(--border);
|
||
margin-top: 10px; flex-shrink: 0; overflow-x: auto;
|
||
}
|
||
.detail-tabs::-webkit-scrollbar { display: none; }
|
||
.dtab {
|
||
padding: 8px 13px; font-size: 12px; font-weight: 500; color: var(--fg3);
|
||
cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px;
|
||
transition: color 0.15s; white-space: nowrap; user-select: none;
|
||
}
|
||
.dtab:hover { color: var(--fg2); }
|
||
.dtab.active { color: var(--fg); border-bottom-color: var(--fg); }
|
||
.detail-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
|
||
.detail-body::-webkit-scrollbar { width: 4px; }
|
||
.detail-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||
.tab-pane { display: none; }
|
||
.tab-pane.active { display: block; }
|
||
|
||
/* Chart tab fills all available height, no scroll needed */
|
||
.tab-pane.chart-tab { display: none; flex-direction: column; height: 100%; }
|
||
.tab-pane.chart-tab.active { display: flex; }
|
||
.detail-body:has(.chart-tab.active) { padding: 0; overflow: hidden; }
|
||
.chart-wrap { display: flex; flex-direction: column; flex: 1; height: 100%; }
|
||
.chart-wrap canvas { display: block; width: 100%; flex: 1; min-height: 0; }
|
||
|
||
/* ── METRICS GRID ──────────────────────────────── */
|
||
.metrics-grid {
|
||
display: grid; grid-template-columns: 1fr 1fr;
|
||
gap: 1px; background: var(--border);
|
||
border: 1px solid var(--border); border-radius: 8px; overflow: hidden;
|
||
margin-bottom: 2px;
|
||
}
|
||
.metrics-grid.cols3 { grid-template-columns: 1fr 1fr 1fr; }
|
||
.mc { background: var(--bg2); padding: 12px 14px; }
|
||
.mc-label { font-size: 10px; color: var(--fg3); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 5px; }
|
||
.mc-val { font-size: 15px; font-weight: 600; cursor: default; }
|
||
.mc-val.pos { color: var(--positive); }
|
||
.mc-val.neg { color: var(--negative); }
|
||
.mc-val.muted { color: var(--fg2); }
|
||
.mc-sub { font-size: 11px; color: var(--fg3); margin-top: 2px; }
|
||
.mc-sub.pos { color: var(--positive); }
|
||
.mc-sub.neg { color: var(--negative); }
|
||
|
||
/* Span full row in a 2-col grid */
|
||
.mc.span2 { grid-column: 1 / -1; }
|
||
|
||
.section-hd { font-size: 10px; font-weight: 700; color: var(--fg3); text-transform: uppercase; letter-spacing: 0.08em; margin: 0 0 10px; }
|
||
|
||
/* ── TOOLTIP ───────────────────────────────────── */
|
||
#tooltip {
|
||
position: fixed; z-index: 9999; pointer-events: none;
|
||
background: #1c1c1c; border: 1px solid #333;
|
||
border-radius: 8px; padding: 10px 13px;
|
||
max-width: 210px; opacity: 0; transition: opacity 0.1s;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
|
||
}
|
||
#tooltip.visible { opacity: 1; }
|
||
.tip-title { font-size: 12px; font-weight: 700; color: var(--fg); margin-bottom: 5px; }
|
||
.tip-divider { height: 1px; background: var(--border); margin-bottom: 7px; }
|
||
.tip-desc { font-size: 11px; color: var(--fg2); line-height: 1.55; margin-bottom: 4px; }
|
||
.tip-context { display: none !important; }
|
||
.tip-context span.hi { color: var(--positive); }
|
||
.tip-context span.lo { color: var(--negative); }
|
||
.tip-context span.warn { color: var(--warning); }
|
||
|
||
/* ── STATUS BAR ────────────────────────────────── */
|
||
.status-bar {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 5px 20px; border-top: 1px solid var(--border);
|
||
background: var(--bg2); flex-shrink: 0;
|
||
}
|
||
.status-l, .status-r { font-size: 11px; color: var(--fg3); }
|
||
|
||
/* ── ANALYST COMMENTARY ────────────────────────── */
|
||
.commentary-box {
|
||
background: var(--bg3); border: 1px solid var(--border);
|
||
border-radius: 8px; padding: 14px 16px; margin-top: 16px;
|
||
}
|
||
.commentary-hd {
|
||
font-size: 10px; font-weight: 700; color: var(--fg3);
|
||
text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 12px;
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.verdict-pill {
|
||
display: inline-block; background: rgba(74,222,128,0.12); color: var(--positive);
|
||
padding: 2px 10px; border-radius: 4px; font-size: 11px; font-weight: 700; text-transform: none;
|
||
}
|
||
.comm-section { margin-bottom: 12px; }
|
||
.comm-title { font-size: 11px; font-weight: 700; color: var(--fg); margin-bottom: 4px; }
|
||
.comm-text { font-size: 12px; color: var(--fg2); line-height: 1.65; }
|
||
.comm-text ul { margin-left: 14px; }
|
||
.comm-text li { margin-bottom: 2px; }
|
||
.comm-link { color: var(--fg2); text-decoration: underline; cursor: pointer; }
|
||
.comm-pos { color: var(--positive); }
|
||
.comm-neg { color: var(--negative); }
|
||
|
||
/* ── CHART ─────────────────────────────────────── */
|
||
.chart-wrap { background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
|
||
.chart-wrap canvas { cursor: crosshair; }
|
||
.chart-period-bar { display: flex; gap: 4px; padding: 10px 14px; border-bottom: 1px solid var(--border); }
|
||
|
||
/* Chart hover tooltip */
|
||
#chart-tip {
|
||
position: fixed; z-index: 9998; pointer-events: none;
|
||
background: #1c1c1c; border: 1px solid #333; border-radius: 8px;
|
||
padding: 9px 13px; min-width: 148px;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,0.7);
|
||
opacity: 0; transition: opacity 0.08s;
|
||
}
|
||
#chart-tip.visible { opacity: 1; }
|
||
.ctip-date { font-size: 11px; color: var(--fg3); margin-bottom: 5px; }
|
||
.ctip-price { font-size: 14px; font-weight: 700; color: var(--fg); }
|
||
.ctip-chg { font-size: 11px; margin-top: 2px; }
|
||
.ctip-vol { font-size: 11px; color: var(--fg3); margin-top: 3px; }
|
||
.period-btn {
|
||
padding: 3px 10px; border-radius: 4px; font-size: 11px; cursor: pointer;
|
||
border: 1px solid var(--border); color: var(--fg3); background: transparent; transition: all 0.1s;
|
||
}
|
||
.period-btn:hover { color: var(--fg2); }
|
||
.period-btn.active { border-color: var(--fg2); color: var(--fg); background: rgba(255,255,255,0.05); }
|
||
|
||
/* ── SEARCH PAGE ───────────────────────────────── */
|
||
.search-idle {
|
||
flex: 1; display: flex; flex-direction: column;
|
||
align-items: center; justify-content: center;
|
||
}
|
||
.search-title { font-size: 26px; font-weight: 800; letter-spacing: -0.03em; margin-bottom: 28px; }
|
||
.search-bar-row { display: flex; gap: 8px; width: 480px; max-width: 90%; }
|
||
.search-input {
|
||
flex: 1; background: var(--bg3); color: var(--fg); border: 1px solid var(--border);
|
||
border-radius: 8px; padding: 11px 16px; font-size: 14px; outline: none; font-family: inherit;
|
||
}
|
||
.search-input:focus { border-color: var(--fg3); }
|
||
.btn-search {
|
||
padding: 11px 22px; background: var(--fg); color: var(--bg);
|
||
border: none; border-radius: 8px; font-size: 13px; font-weight: 600;
|
||
cursor: pointer; font-family: inherit;
|
||
}
|
||
.search-hint { margin-top: 10px; font-size: 12px; color: var(--fg3); }
|
||
|
||
/* ── TRACKING ──────────────────────────────────── */
|
||
.tracking-subnav {
|
||
display: flex; gap: 8px; padding: 11px 16px;
|
||
border-bottom: 1px solid var(--border); background: var(--bg2); flex-shrink: 0;
|
||
}
|
||
.snav-btn {
|
||
padding: 5px 16px; border-radius: 6px; font-size: 12px; font-weight: 500;
|
||
cursor: pointer; border: 1px solid var(--border); color: var(--fg3);
|
||
background: transparent; transition: all 0.1s; font-family: inherit;
|
||
}
|
||
.snav-btn.active { background: var(--fg); color: var(--bg); border-color: var(--fg); }
|
||
.tracking-toolbar {
|
||
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
|
||
padding: 9px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0;
|
||
}
|
||
.tr-label { font-size: 12px; font-weight: 600; color: var(--fg2); margin-right: 4px; }
|
||
.tr-sep { width: 1px; height: 16px; background: var(--border); margin: 0 4px; }
|
||
.tperiod-btn {
|
||
padding: 3px 9px; border-radius: 4px; font-size: 11px; cursor: pointer;
|
||
border: 1px solid transparent; color: var(--fg3); background: transparent;
|
||
}
|
||
.tperiod-btn.active { border-color: var(--fg3); color: var(--fg); }
|
||
.btn-refresh {
|
||
margin-left: auto; padding: 4px 12px; border-radius: 4px; font-size: 11px;
|
||
cursor: pointer; border: 1px solid var(--border); color: var(--fg2);
|
||
background: transparent; font-family: inherit;
|
||
}
|
||
.tr-status { font-size: 11px; color: var(--fg3); }
|
||
.tracking-table-scroll { flex: 1; overflow-y: auto; }
|
||
.tracking-table-scroll::-webkit-scrollbar { width: 4px; }
|
||
.tracking-table-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||
table.tr-table { width: 100%; border-collapse: collapse; }
|
||
table.tr-table thead th {
|
||
padding: 6px 14px; text-align: left; font-size: 10px; font-weight: 700; color: var(--fg3);
|
||
text-transform: uppercase; letter-spacing: 0.06em; border-bottom: 1px solid var(--border);
|
||
background: var(--bg); position: sticky; top: 0; z-index: 1;
|
||
}
|
||
table.tr-table thead th.r { text-align: right; }
|
||
table.tr-table tbody tr { cursor: pointer; }
|
||
table.tr-table tbody tr:nth-child(even) { background: rgba(255,255,255,0.02); }
|
||
table.tr-table tbody tr:hover { background: #1c1c1c; }
|
||
table.tr-table tbody td { padding: 8px 14px; font-size: 12px; }
|
||
table.tr-table tbody td.r { text-align: right; }
|
||
table.tr-table tbody td.muted { color: var(--fg2); }
|
||
table.tr-table tbody td.dimmed { color: var(--fg3); }
|
||
.type-buy { color: var(--positive); font-weight: 600; }
|
||
.type-sale { color: var(--negative); font-weight: 600; }
|
||
.type-award { color: var(--warning); font-weight: 600; }
|
||
.tpage { display: none; flex: 1; flex-direction: column; overflow: hidden; min-height: 0; }
|
||
.tpage.active { display: flex; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ── TOOLTIP ──────────────────────────────────── -->
|
||
<div id="tooltip">
|
||
<div class="tip-title" id="tip-title"></div>
|
||
<div class="tip-divider"></div>
|
||
<div class="tip-desc" id="tip-desc"></div>
|
||
<div class="tip-context" id="tip-context"></div>
|
||
</div>
|
||
|
||
<!-- ── CHART HOVER TIP ───────────────────────────── -->
|
||
<div id="chart-tip">
|
||
<div class="ctip-date" id="ctip-date"></div>
|
||
<div class="ctip-price" id="ctip-price"></div>
|
||
<div class="ctip-chg" id="ctip-chg"></div>
|
||
<div class="ctip-vol" id="ctip-vol"></div>
|
||
</div>
|
||
|
||
<div class="app">
|
||
|
||
<!-- ── SIDEBAR ────────────────────────────────── -->
|
||
<div class="sidebar">
|
||
<div class="sidebar-logo">V</div>
|
||
<div class="sidebar-nav">
|
||
<div class="nav-item active" onclick="setPage('screener',this)">
|
||
<span class="nav-icon">⊞</span><span class="nav-label">Screener</span>
|
||
</div>
|
||
<div class="nav-item" onclick="setPage('search',this)">
|
||
<span class="nav-icon">⌕</span><span class="nav-label">Search</span>
|
||
</div>
|
||
<div class="nav-item" onclick="setPage('tracking',this)">
|
||
<span class="nav-icon">↗</span><span class="nav-label">Tracking</span>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-bottom">
|
||
<div class="nav-item" onclick="setPage('settings',this)">
|
||
<span class="nav-icon">⚙</span><span class="nav-label">Settings</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── MAIN ───────────────────────────────────── -->
|
||
<div class="main">
|
||
|
||
<!-- ══════════════ SCREENER PAGE ══════════════ -->
|
||
<div class="page active" id="page-screener">
|
||
<div class="toolbar">
|
||
<!-- Row 1: actions + universe filters -->
|
||
<div class="toolbar-row">
|
||
<div class="pill-group">
|
||
<button class="pill-btn run">▶ Run</button>
|
||
<button class="pill-btn stop">■ Stop</button>
|
||
</div>
|
||
<div class="pill-group">
|
||
<select class="pill-sel">
|
||
<option>S&P 500</option><option>Russell 2000</option>
|
||
<option>Nasdaq 100</option><option>Dow Jones 30</option>
|
||
</select>
|
||
<select class="pill-sel">
|
||
<option>All Sectors</option><option>Technology</option>
|
||
<option>Financials</option><option>Healthcare</option>
|
||
<option>Energy</option><option>Industrials</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<!-- Row 2: strategy + display + filters -->
|
||
<div class="toolbar-row">
|
||
<div class="pill-group">
|
||
<select class="pill-sel" style="min-width:110px">
|
||
<option>Balanced</option><option>Value</option>
|
||
<option>Growth</option><option>Momentum</option>
|
||
<option>Quality</option><option>Dividend</option>
|
||
<option>Custom…</option>
|
||
</select>
|
||
<select class="pill-sel" style="min-width:68px">
|
||
<option>100</option><option>200</option>
|
||
<option>300</option><option>500</option><option>All</option>
|
||
</select>
|
||
</div>
|
||
<div class="pill-solo">⚙ Filters</div>
|
||
<span class="toolbar-ts">Last run: 4 mins ago</span>
|
||
</div>
|
||
</div>
|
||
<div class="progress-wrap"><div class="progress-fill" style="width:100%"></div></div>
|
||
|
||
<div class="content-split">
|
||
|
||
<!-- Stock list -->
|
||
<div class="stock-list">
|
||
<div class="list-header">
|
||
<span class="list-header-title">Results</span>
|
||
<span class="list-header-count">87 stocks</span>
|
||
</div>
|
||
<div class="stock-table-scroll">
|
||
<table class="stocks">
|
||
<thead><tr><th>#</th><th>Ticker</th><th>Name</th><th class="r">Score</th></tr></thead>
|
||
<tbody id="stock-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Detail panel -->
|
||
<div class="detail-panel">
|
||
<div class="detail-empty" id="detail-empty">← Select a stock to view details</div>
|
||
<div class="detail-filled" id="detail-filled" style="display:none">
|
||
<div class="detail-header">
|
||
<div class="detail-ticker-row">
|
||
<span class="detail-ticker" id="d-ticker"></span>
|
||
<span class="detail-name" id="d-name"></span>
|
||
<span class="detail-sector" id="d-sector"></span>
|
||
</div>
|
||
</div>
|
||
<div class="detail-tabs" id="detail-tabs">
|
||
<div class="dtab active" onclick="setTab('scores',this,'detail-tabs','detail-body')">Scores</div>
|
||
<div class="dtab" onclick="setTab('valuation',this,'detail-tabs','detail-body')">Valuation</div>
|
||
<div class="dtab" onclick="setTab('growth',this,'detail-tabs','detail-body')">Growth</div>
|
||
<div class="dtab" onclick="setTab('momentum',this,'detail-tabs','detail-body')">Momentum</div>
|
||
<div class="dtab" onclick="setTab('quality',this,'detail-tabs','detail-body')">Quality & Profit</div>
|
||
<div class="dtab" onclick="setTab('analyst',this,'detail-tabs','detail-body')">Analyst</div>
|
||
<div class="dtab" onclick="setTab('chart',this,'detail-tabs','detail-body')">Chart</div>
|
||
</div>
|
||
<div class="detail-body" id="detail-body">
|
||
|
||
<!-- SCORES -->
|
||
<div class="tab-pane active" id="tab-scores">
|
||
<div class="metrics-grid span-first" style="grid-template-columns:1fr">
|
||
<div class="mc"
|
||
data-tip="Composite Score|Weighted aggregate of all 8 scoring factors — value, growth, momentum, quality, profitability, sentiment, analyst, and risk.|Higher is always better. 80+ = strong buy candidate. 50 = sector median."
|
||
>
|
||
<div class="mc-label">Composite Score</div>
|
||
<div class="mc-val pos" id="sc-composite">82</div>
|
||
<div class="mc-sub pos" id="sc-composite-tier">Excellent</div>
|
||
</div>
|
||
</div>
|
||
<div style="height:1px"></div>
|
||
<div class="metrics-grid" id="scores-grid">
|
||
<div class="mc" data-tip="Value Score|How cheaply the stock trades relative to its fundamentals — P/E, P/B, EV/EBITDA, EV/Revenue — all normalized against sector peers.|50 = sector median. Above 70 = attractively priced. Below 40 = expensive vs. peers.">
|
||
<div class="mc-label">Value</div>
|
||
<div class="mc-val" id="sc-value">54</div>
|
||
<div class="mc-sub" id="sc-value-tier">Moderate</div>
|
||
</div>
|
||
<div class="mc" data-tip="Growth Score|Revenue growth, earnings growth, and EPS expansion — all z-scored against sector peers and blended by strategy weight.|50 = sector median. Above 70 = top-quartile grower.">
|
||
<div class="mc-label">Growth</div>
|
||
<div class="mc-val" id="sc-growth">73</div>
|
||
<div class="mc-sub pos" id="sc-growth-tier">Strong</div>
|
||
</div>
|
||
<div class="mc" data-tip="Momentum Score|Price returns over 1M, 3M, 6M, and 12M, each weighted and z-scored against sector. Captures trend persistence.|50 = sector median. Scores above 75 indicate sustained relative outperformance.">
|
||
<div class="mc-label">Momentum</div>
|
||
<div class="mc-val" id="sc-momentum">62</div>
|
||
<div class="mc-sub" id="sc-momentum-tier">Moderate</div>
|
||
</div>
|
||
<div class="mc" data-tip="Quality Score|Balance sheet strength: ROA, ROE, debt-to-equity ratio, current ratio, and Piotroski F-Score (0–9 binary checklist).|50 = sector median. Piotroski 8–9 = financially sound. D/E normalized against sector type.">
|
||
<div class="mc-label">Quality</div>
|
||
<div class="mc-val" id="sc-quality">79</div>
|
||
<div class="mc-sub pos" id="sc-quality-tier">Strong</div>
|
||
</div>
|
||
<div class="mc" data-tip="Profitability Score|Operating margin, profit margin, FCF yield, and Sloan accruals ratio (measures earnings quality — cash-backed vs. accrual-based income).|50 = sector median. Negative accruals ratio = earnings well-backed by cash flow.">
|
||
<div class="mc-label">Profitability</div>
|
||
<div class="mc-val" id="sc-profit">65</div>
|
||
<div class="mc-sub" id="sc-profit-tier">Moderate</div>
|
||
</div>
|
||
<div class="mc" data-tip="Sentiment Score|News sentiment score (NLP on recent headlines) weighted by headline volume, combined with analyst consensus direction.|Low headline count reduces score confidence. Score above 70 = broadly positive narrative.">
|
||
<div class="mc-label">Sentiment</div>
|
||
<div class="mc-val" id="sc-sentiment">71</div>
|
||
<div class="mc-sub pos" id="sc-sentiment-tier">Strong</div>
|
||
</div>
|
||
<div class="mc" data-tip="Analyst Score|Analyst consensus recommendation strength, number of covering analysts, and % upside to consensus price target.|More analysts = higher confidence. Upside > 15% with Buy consensus = high score.">
|
||
<div class="mc-label">Analyst</div>
|
||
<div class="mc-val muted" id="sc-analyst">55</div>
|
||
<div class="mc-sub" id="sc-analyst-tier">Moderate</div>
|
||
</div>
|
||
<div class="mc" data-tip="Risk Score|Inverse of volatility and market beta. Higher score = lower risk profile. Short interest % of float also penalizes score.|Score above 70 = low-volatility, low-beta profile. Below 40 = elevated risk / high short interest.">
|
||
<div class="mc-label">Risk</div>
|
||
<div class="mc-val" id="sc-risk">68</div>
|
||
<div class="mc-sub" id="sc-risk-tier">Moderate</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- VALUATION -->
|
||
<div class="tab-pane" id="tab-valuation">
|
||
<p class="section-hd">Valuation Metrics</p>
|
||
<div class="metrics-grid">
|
||
<div class="mc" data-tip="Price|Current last-traded market price per share.">
|
||
<div class="mc-label">Price</div><div class="mc-val">$213.49</div></div>
|
||
<div class="mc" data-tip="Market Capitalization|Total shares outstanding × current price. Determines index eligibility and institutional coverage.">
|
||
<div class="mc-label">Market Cap</div><div class="mc-val">$3.28T</div></div>
|
||
<div class="mc" data-tip="P/E Forward|Price divided by consensus forward 12-month EPS estimate. Primary valuation metric used in scoring.|Lower = cheaper. Sector median shown in score context.">
|
||
<div class="mc-label">P/E Forward</div><div class="mc-val">28.4×</div></div>
|
||
<div class="mc" data-tip="P/E Trailing|Price divided by trailing 12-month (TTM) actual earnings per share.|Useful for comparing to forward P/E — a large gap suggests analyst optimism.">
|
||
<div class="mc-label">P/E Trailing</div><div class="mc-val muted">32.1×</div></div>
|
||
<div class="mc" data-tip="P/B Ratio|Price divided by book value per share. High P/B is common for asset-light businesses with strong intangibles.">
|
||
<div class="mc-label">P/B Ratio</div><div class="mc-val">47.2×</div></div>
|
||
<div class="mc" data-tip="Book Value Per Share|Total shareholder equity divided by shares outstanding. Theoretical liquidation value per share.">
|
||
<div class="mc-label">Book Value</div><div class="mc-val muted">$4.52</div></div>
|
||
<div class="mc" data-tip="EV / EBITDA|Enterprise value divided by EBITDA. Capital-structure-neutral valuation — better than P/E for comparing companies with different leverage.">
|
||
<div class="mc-label">EV / EBITDA</div><div class="mc-val">22.3×</div></div>
|
||
<div class="mc" data-tip="EV / Revenue|Enterprise value divided by trailing revenue. Used for early-stage or low-margin companies where EBITDA is small or negative.">
|
||
<div class="mc-label">EV / Revenue</div><div class="mc-val">8.4×</div></div>
|
||
<div class="mc" data-tip="52-Week High|Highest intraday price over the trailing 52 weeks. Current price at 90% of 52W high.">
|
||
<div class="mc-label">52W High</div><div class="mc-val muted">$237.23</div></div>
|
||
<div class="mc" data-tip="52-Week Low|Lowest intraday price over the trailing 52 weeks. Current price is 30% above the 52W low.">
|
||
<div class="mc-label">52W Low</div><div class="mc-val muted">$164.08</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- GROWTH -->
|
||
<div class="tab-pane" id="tab-growth">
|
||
<p class="section-hd">Growth Metrics</p>
|
||
<div class="metrics-grid">
|
||
<div class="mc" data-tip="Revenue Growth|Year-over-year change in trailing 12-month revenue. Sourced from EDGAR XBRL frames.|Above sector median contributes to a higher Growth score.">
|
||
<div class="mc-label">Revenue Growth</div><div class="mc-val pos">+4.2%</div></div>
|
||
<div class="mc" data-tip="Earnings Growth|Year-over-year change in trailing 12-month net income.|Compared against sector peers in z-score normalization.">
|
||
<div class="mc-label">Earnings Growth</div><div class="mc-val pos">+8.7%</div></div>
|
||
<div class="mc" data-tip="EPS Forward|Consensus analyst estimate for earnings per share over the next 12 months.|Used in forward P/E calculation and growth trajectory assessment.">
|
||
<div class="mc-label">EPS Forward</div><div class="mc-val">$7.52</div></div>
|
||
<div class="mc" data-tip="EPS Trailing|Actual earnings per share over the trailing 12 months (TTM).|Comparing to forward EPS: +13% implied growth expected by analysts.">
|
||
<div class="mc-label">EPS Trailing</div><div class="mc-val muted">$6.65</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MOMENTUM -->
|
||
<div class="tab-pane" id="tab-momentum">
|
||
<p class="section-hd">Price Returns</p>
|
||
<div class="metrics-grid">
|
||
<div class="mc" data-tip="1-Month Return|Total price return over the trailing 1 calendar month.|Short-term momentum signal. Weighted at 15% of the Momentum score.">
|
||
<div class="mc-label">1-Month Return</div><div class="mc-val pos">+3.2%</div></div>
|
||
<div class="mc" data-tip="3-Month Return|Total price return over the trailing 3 months.|Medium-term momentum. Weighted at 25% of the Momentum score.">
|
||
<div class="mc-label">3-Month Return</div><div class="mc-val neg">−1.4%</div></div>
|
||
<div class="mc" data-tip="6-Month Return|Total price return over the trailing 6 months.|Primary momentum window. Weighted at 35% of the Momentum score.">
|
||
<div class="mc-label">6-Month Return</div><div class="mc-val pos">+8.9%</div></div>
|
||
<div class="mc" data-tip="12-Month Return|Total price return over the trailing 12 months.|Long-term momentum. Weighted at 25% of the Momentum score.">
|
||
<div class="mc-label">12-Month Return</div><div class="mc-val pos">+18.3%</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- QUALITY & PROFIT -->
|
||
<div class="tab-pane" id="tab-quality">
|
||
<p class="section-hd">Income & Cash Flow</p>
|
||
<div class="metrics-grid" style="margin-bottom:1px">
|
||
<div class="mc" data-tip="Revenue (TTM)|Trailing 12-month total revenue. Sum of 4 most recent quarters from EDGAR XBRL.|Minimum 4 quarters required — partial-year totals excluded to avoid distortion.">
|
||
<div class="mc-label">Revenue (TTM)</div><div class="mc-val">$391.0B</div></div>
|
||
<div class="mc" data-tip="Net Income|Trailing 12-month net income after tax. Revenue × net profit margin.">
|
||
<div class="mc-label">Net Income</div><div class="mc-val">$93.7B</div></div>
|
||
<div class="mc" data-tip="Operating Income|Earnings before interest and taxes (EBIT) over trailing 12 months.|Excludes one-time items. Better measure of recurring business profitability.">
|
||
<div class="mc-label">Operating Income</div><div class="mc-val">$108.9B</div></div>
|
||
<div class="mc" data-tip="Total Cash|Cash and short-term investments on the most recent balance sheet.|Apple holds $61.8B cash vs. $96.8B debt — net debt of $35.0B.">
|
||
<div class="mc-label">Total Cash</div><div class="mc-val">$61.8B</div></div>
|
||
</div>
|
||
<p class="section-hd" style="margin-top:14px">Balance Sheet</p>
|
||
<div class="metrics-grid" style="margin-bottom:1px">
|
||
<div class="mc" data-tip="Total Debt|Total short-term + long-term debt obligations from the most recent balance sheet.">
|
||
<div class="mc-label">Total Debt</div><div class="mc-val muted">$96.8B</div></div>
|
||
<div class="mc" data-tip="Total Equity|Total shareholder equity (assets minus liabilities). Derived from D/E ratio.|Note: Apple's equity is low relative to its earnings power — common for buyback-heavy companies.">
|
||
<div class="mc-label">Total Equity</div><div class="mc-val muted">$67.0B</div></div>
|
||
<div class="mc" data-tip="Current Ratio|Current assets divided by current liabilities. Below 1.0 means more short-term liabilities than assets.|Apple's 0.87 is below 1 — normal for a company with large supplier payables and predictable cash generation.">
|
||
<div class="mc-label">Current Ratio</div><div class="mc-val neg">0.87</div></div>
|
||
<div class="mc" data-tip="Debt / Equity Ratio|Total debt divided by total equity. Higher = more leveraged. Normalized against sector median in Quality scoring.|yFinance D/E is scaled (÷100) to match EDGAR ratio format before comparison.">
|
||
<div class="mc-label">D/E Ratio</div><div class="mc-val muted">1.44</div></div>
|
||
</div>
|
||
<p class="section-hd" style="margin-top:14px">Margins & Returns</p>
|
||
<div class="metrics-grid">
|
||
<div class="mc" data-tip="Return on Equity|Net income divided by average shareholder equity. Apple's 147% reflects both high profitability and aggressive buybacks reducing equity base.">
|
||
<div class="mc-label">ROE</div><div class="mc-val pos">147.3%</div></div>
|
||
<div class="mc" data-tip="Return on Assets|Net income divided by total assets. Measures how efficiently management uses assets to generate profit.">
|
||
<div class="mc-label">ROA</div><div class="mc-val pos">22.1%</div></div>
|
||
<div class="mc" data-tip="Profit Margin|Net income as a percentage of revenue (TTM). Used in Profitability score z-scoring vs. sector.|$93.7B net income on $391.0B revenue.">
|
||
<div class="mc-label">Profit Margin</div><div class="mc-val pos">23.97%</div><div class="mc-sub">$93.7B net</div></div>
|
||
<div class="mc" data-tip="Operating Margin|Operating income as a percentage of revenue. Excludes tax and interest — pure business efficiency.|$108.9B operating income on $391.0B revenue.">
|
||
<div class="mc-label">Operating Margin</div><div class="mc-val pos">27.84%</div><div class="mc-sub">$108.9B op.</div></div>
|
||
<div class="mc" data-tip="Free Cash Flow Yield|Free cash flow (operating CF − capex) divided by market cap. Measures cash generation relative to price.|3.8% FCF yield is moderate for a mega-cap. Sector median ~2.4%.">
|
||
<div class="mc-label">FCF Yield</div><div class="mc-val">3.8%</div></div>
|
||
<div class="mc" data-tip="Dividend Yield|Annual dividend per share divided by current price.|Apple pays $1.00/share annually. Yield is intentionally low — capital return is primarily via buybacks ($90B+ annually).">
|
||
<div class="mc-label">Dividend Yield</div><div class="mc-val muted">0.51%</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ANALYST -->
|
||
<div class="tab-pane" id="tab-analyst">
|
||
<p class="section-hd">Analyst & Market Data</p>
|
||
<div class="metrics-grid">
|
||
<div class="mc" data-tip="Analyst Recommendation|Consensus buy/hold/sell rating aggregated across all covering analysts.|Buy consensus with 34 analysts = high-confidence signal.">
|
||
<div class="mc-label">Recommendation</div><div class="mc-val pos">Buy</div></div>
|
||
<div class="mc" data-tip="Analyst Count|Number of sell-side analysts actively covering this stock.|More analysts = higher signal confidence. <5 analysts reduces the Analyst score weight.">
|
||
<div class="mc-label"># Analysts</div><div class="mc-val">34</div></div>
|
||
<div class="mc" data-tip="Consensus Price Target|Median analyst 12-month price target across all covering analysts.|$241.00 vs current $213.49 = 12.9% implied upside.">
|
||
<div class="mc-label">Price Target</div><div class="mc-val">$241.00</div></div>
|
||
<div class="mc" data-tip="Upside to Target|Percentage difference between consensus price target and current price.|(target / price − 1) × 100. Positive = underpriced vs. analyst consensus.">
|
||
<div class="mc-label">Upside to Target</div><div class="mc-val pos">+12.9%</div></div>
|
||
<div class="mc" data-tip="News Sentiment|NLP sentiment score derived from recent news headlines, weighted by headline volume.|Range 0–1. Above 0.6 = broadly positive narrative. Score confidence scales with headline count.">
|
||
<div class="mc-label">News Sentiment</div><div class="mc-val">0.72</div></div>
|
||
<div class="mc" data-tip="Short Interest|Percentage of float currently sold short (shortPercentOfFloat from yFinance).|Low short interest (0.8%) = limited bearish positioning. Above 10% = meaningful headwind.">
|
||
<div class="mc-label">Short Interest</div><div class="mc-val muted">0.8%</div></div>
|
||
<div class="mc" data-tip="Beta|Sensitivity of the stock's returns to the broader market (S&P 500 = 1.0).|1.24 = ~24% more volatile than the market. Used in Risk score calculation.">
|
||
<div class="mc-label">Beta</div><div class="mc-val muted">1.24</div></div>
|
||
<div class="mc" data-tip="30-Day Volatility|Annualized standard deviation of daily returns over the trailing 30 calendar days.|22.4% annualized = roughly 1.4% expected daily move (±1 std dev).">
|
||
<div class="mc-label">30D Volatility</div><div class="mc-val muted">22.4%</div></div>
|
||
</div>
|
||
<div class="commentary-box">
|
||
<div class="commentary-hd">Analyst Commentary <span class="verdict-pill">Strong Buy</span></div>
|
||
<div class="comm-section">
|
||
<div class="comm-title">SUMMARY</div>
|
||
<div class="comm-text">Apple is a dominant consumer-tech franchise with exceptional cash generation and expanding services margin. Near-term headwinds from China exposure and a stretched P/E are offset by the compounding services flywheel and consistent capital return program.</div>
|
||
</div>
|
||
<div class="comm-section">
|
||
<div class="comm-title">INVESTMENT THESIS</div>
|
||
<div class="comm-text"><ul>
|
||
<li><span class="comm-pos">Services revenue accelerating</span> — App Store, Apple TV+, iCloud growing at 12–15% YoY</li>
|
||
<li><span class="comm-pos">FCF machine</span> — $100B+ annual free cash flow funds buybacks and dividends</li>
|
||
<li><span class="comm-pos">ROE of 147%</span> indicates exceptional capital efficiency relative to peers</li>
|
||
<li>iPhone installed base of 1.4B provides structural defensibility</li>
|
||
</ul></div>
|
||
</div>
|
||
<div class="comm-section">
|
||
<div class="comm-title">RISK FACTORS</div>
|
||
<div class="comm-text"><ul>
|
||
<li><span class="comm-neg">China revenue (17% of total)</span> — regulatory and tariff exposure</li>
|
||
<li><span class="comm-neg">P/E of 32×</span> leaves limited margin of safety at current price</li>
|
||
<li>Hardware saturation risk in mature iPhone markets</li>
|
||
</ul></div>
|
||
</div>
|
||
<div class="comm-section">
|
||
<div class="comm-title">NEWS HIGHLIGHTS</div>
|
||
<div class="comm-text">
|
||
<span class="comm-pos">+</span> <span class="comm-link">Apple Intelligence features drive record upgrade cycle — Bloomberg</span><br>
|
||
<span class="comm-pos">+</span> <span class="comm-link">Services segment posts 14% growth beating estimates — Reuters</span><br>
|
||
<span class="comm-neg">–</span> <span class="comm-link">DOJ widens antitrust probe into App Store fees — WSJ</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CHART -->
|
||
<div class="tab-pane chart-tab" id="tab-chart">
|
||
<div class="chart-wrap">
|
||
<div class="chart-period-bar" id="cpbar1">
|
||
<div class="period-btn">1M</div><div class="period-btn">3M</div>
|
||
<div class="period-btn active">6M</div><div class="period-btn">1Y</div>
|
||
<div class="period-btn">2Y</div><div class="period-btn">5Y</div>
|
||
</div>
|
||
<canvas id="price-chart"></canvas>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /detail-body -->
|
||
</div><!-- /detail-filled -->
|
||
</div><!-- /detail-panel -->
|
||
</div><!-- /content-split -->
|
||
|
||
<div class="status-bar">
|
||
<span class="status-l">Showing 87 of 500 stocks · Click a ticker to see details</span>
|
||
<span class="status-r">Cache: 4,200 stocks · updated 4 mins ago</span>
|
||
</div>
|
||
</div><!-- /screener page -->
|
||
|
||
<!-- ══════════════ SEARCH PAGE ══════════════ -->
|
||
<div class="page" id="page-search">
|
||
<div class="search-idle" id="search-idle">
|
||
<div class="search-title">Verimund Stock Tool</div>
|
||
<div class="search-bar-row">
|
||
<input class="search-input" type="text" placeholder="Search by ticker or company name…"
|
||
id="search-inp" onkeydown="if(event.key==='Enter')doSearch()">
|
||
<button class="btn-search" onclick="doSearch()">Search</button>
|
||
</div>
|
||
<div class="search-hint">Try: AAPL, NVDA, Microsoft, Tesla…</div>
|
||
</div>
|
||
<div class="detail-filled" id="search-result" style="display:none">
|
||
<div class="detail-header">
|
||
<div class="detail-ticker-row">
|
||
<span class="detail-ticker">NVDA</span>
|
||
<span class="detail-name">Nvidia Corp</span>
|
||
<span class="detail-sector">· Technology</span>
|
||
</div>
|
||
<div style="margin-bottom:4px">
|
||
<span style="font-size:11px;color:var(--fg3);cursor:pointer;border:1px solid var(--border);padding:3px 10px;border-radius:5px"
|
||
onclick="document.getElementById('search-idle').style.display='flex';document.getElementById('search-result').style.display='none'">← Back</span>
|
||
</div>
|
||
</div>
|
||
<div class="detail-tabs" id="search-tabs">
|
||
<div class="dtab active" onclick="setTab('scores2',this,'search-tabs','search-body')">Scores</div>
|
||
<div class="dtab" onclick="setTab('valuation2',this,'search-tabs','search-body')">Valuation</div>
|
||
<div class="dtab" onclick="setTab('growth2',this,'search-tabs','search-body')">Growth</div>
|
||
<div class="dtab" onclick="setTab('momentum2',this,'search-tabs','search-body')">Momentum</div>
|
||
<div class="dtab" onclick="setTab('quality2',this,'search-tabs','search-body')">Quality & Profit</div>
|
||
<div class="dtab" onclick="setTab('analyst2',this,'search-tabs','search-body')">Analyst</div>
|
||
<div class="dtab" onclick="setTab('chart2',this,'search-tabs','search-body')">Chart</div>
|
||
</div>
|
||
<div class="detail-body" id="search-body">
|
||
|
||
<div class="tab-pane active" id="tab-scores2">
|
||
<div class="metrics-grid" style="grid-template-columns:1fr">
|
||
<div class="mc" data-tip="Composite Score|Weighted aggregate of all 8 scoring factors.|91/100 — Excellent. Top 5% of S&P 500 by composite rank.">
|
||
<div class="mc-label">Composite Score</div>
|
||
<div class="mc-val pos">91</div>
|
||
<div class="mc-sub pos">Excellent</div>
|
||
</div>
|
||
</div>
|
||
<div style="height:1px"></div>
|
||
<div class="metrics-grid">
|
||
<div class="mc" data-tip="Value Score|P/E, P/B, EV/EBITDA vs. sector peers.|42 — Below sector median. NVDA trades at a significant premium to the tech sector average, reflecting growth expectations.">
|
||
<div class="mc-label">Value</div><div class="mc-val neg">42</div><div class="mc-sub neg">Weak</div></div>
|
||
<div class="mc" data-tip="Growth Score|Revenue +122% YoY, earnings +581% YoY — extreme outlier vs. sector.|94 — Exceptional. Top 2% of universe.">
|
||
<div class="mc-label">Growth</div><div class="mc-val pos">94</div><div class="mc-sub pos">Exceptional</div></div>
|
||
<div class="mc" data-tip="Momentum Score|12M return +89.3%, 6M +38.7% — sustained outperformance.|88 — Strong. Multi-timeframe momentum alignment.">
|
||
<div class="mc-label">Momentum</div><div class="mc-val pos">88</div><div class="mc-sub pos">Strong</div></div>
|
||
<div class="mc" data-tip="Quality Score|ROE 123%, ROA 55%, low D/E ratio, strong Piotroski F-Score.|85 — Excellent balance sheet and capital efficiency.">
|
||
<div class="mc-label">Quality</div><div class="mc-val pos">85</div><div class="mc-sub pos">Excellent</div></div>
|
||
<div class="mc" data-tip="Profitability Score|48.9% profit margin, FCF yield 2.1%, strong accruals quality.|91 — Top-quartile profitability in the technology sector.">
|
||
<div class="mc-label">Profitability</div><div class="mc-val pos">91</div><div class="mc-sub pos">Exceptional</div></div>
|
||
<div class="mc" data-tip="Sentiment Score|Broadly positive news cycle, 62 analysts covering, strong buy consensus.|86 — Very positive sentiment across news and analyst community.">
|
||
<div class="mc-label">Sentiment</div><div class="mc-val pos">86</div><div class="mc-sub pos">Strong</div></div>
|
||
<div class="mc" data-tip="Analyst Score|62 analysts, Strong Buy consensus, $1,050 target = +19.9% upside.|92 — Deep coverage with very strong conviction.">
|
||
<div class="mc-label">Analyst</div><div class="mc-val pos">92</div><div class="mc-sub pos">Excellent</div></div>
|
||
<div class="mc" data-tip="Risk Score|Beta 1.71, elevated 30D volatility, significant drawdown risk if AI sentiment shifts.|44 — High-beta, high-volatility profile. Elevated risk for conservative portfolios.">
|
||
<div class="mc-label">Risk</div><div class="mc-val neg">44</div><div class="mc-sub neg">Elevated</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tab-pane" id="tab-valuation2">
|
||
<p class="section-hd">Valuation Metrics</p>
|
||
<div class="metrics-grid">
|
||
<div class="mc" data-tip="Price|Current last-traded market price per share."><div class="mc-label">Price</div><div class="mc-val">$875.40</div></div>
|
||
<div class="mc" data-tip="Market Capitalization|Total shares outstanding × current price."><div class="mc-label">Market Cap</div><div class="mc-val">$2.15T</div></div>
|
||
<div class="mc" data-tip="P/E Forward|38.2× — premium to sector but supported by +122% revenue growth."><div class="mc-label">P/E Forward</div><div class="mc-val">38.2×</div></div>
|
||
<div class="mc" data-tip="P/E Trailing|56.4× trailing — high, reflecting how fast earnings have grown."><div class="mc-label">P/E Trailing</div><div class="mc-val muted">56.4×</div></div>
|
||
<div class="mc" data-tip="EV / EBITDA|46.7× — elevated, justified by dominant AI accelerator position."><div class="mc-label">EV / EBITDA</div><div class="mc-val">46.7×</div></div>
|
||
<div class="mc" data-tip="P/B Ratio|54.1× — asset-light GPU design company; book value understates true value."><div class="mc-label">P/B Ratio</div><div class="mc-val">54.1×</div></div>
|
||
<div class="mc" data-tip="52-Week High|$974.00 — current price is 10% below 52W high."><div class="mc-label">52W High</div><div class="mc-val muted">$974.00</div></div>
|
||
<div class="mc" data-tip="52-Week Low|$462.17 — current price is 89% above 52W low."><div class="mc-label">52W Low</div><div class="mc-val muted">$462.17</div></div>
|
||
</div>
|
||
</div>
|
||
<div class="tab-pane" id="tab-growth2"><p class="section-hd">Growth Metrics</p><div class="metrics-grid"><div class="mc" data-tip="Revenue Growth|+122.4% YoY — driven by H100/H200 GPU data center demand."><div class="mc-label">Revenue Growth</div><div class="mc-val pos">+122.4%</div></div><div class="mc" data-tip="Earnings Growth|+581% YoY net income growth on operating leverage."><div class="mc-label">Earnings Growth</div><div class="mc-val pos">+581%</div></div><div class="mc" data-tip="EPS Forward|Consensus FY2025 estimate. Implies continued strong GPU demand."><div class="mc-label">EPS Forward</div><div class="mc-val">$22.89</div></div><div class="mc" data-tip="EPS Trailing|TTM actual. Forward/trailing gap implies 47% expected EPS growth."><div class="mc-label">EPS Trailing</div><div class="mc-val muted">$15.52</div></div></div></div>
|
||
<div class="tab-pane" id="tab-momentum2"><p class="section-hd">Price Returns</p><div class="metrics-grid"><div class="mc" data-tip="1-Month Return|+6.8% — short-term momentum intact."><div class="mc-label">1-Month Return</div><div class="mc-val pos">+6.8%</div></div><div class="mc" data-tip="3-Month Return|+14.2% — sustained medium-term strength."><div class="mc-label">3-Month Return</div><div class="mc-val pos">+14.2%</div></div><div class="mc" data-tip="6-Month Return|+38.7% — primary momentum window."><div class="mc-label">6-Month Return</div><div class="mc-val pos">+38.7%</div></div><div class="mc" data-tip="12-Month Return|+89.3% — one of the best performers in the S&P 500."><div class="mc-label">12-Month Return</div><div class="mc-val pos">+89.3%</div></div></div></div>
|
||
<div class="tab-pane" id="tab-quality2"><p class="section-hd">Quality & Profitability</p><div class="metrics-grid"><div class="mc" data-tip="Revenue (TTM)|Trailing 12M revenue sourced from EDGAR XBRL."><div class="mc-label">Revenue (TTM)</div><div class="mc-val">$60.9B</div></div><div class="mc" data-tip="Net Income|TTM net income — 48.9% profit margin."><div class="mc-label">Net Income</div><div class="mc-val pos">$29.8B</div></div><div class="mc" data-tip="Return on Equity|123.4% — exceptional capital efficiency."><div class="mc-label">ROE</div><div class="mc-val pos">123.4%</div></div><div class="mc" data-tip="Return on Assets|55.2% — among highest in large-cap tech."><div class="mc-label">ROA</div><div class="mc-val pos">55.2%</div></div><div class="mc" data-tip="Profit Margin|48.9% net margin — premium fabless semiconductor margin."><div class="mc-label">Profit Margin</div><div class="mc-val pos">48.9%</div></div><div class="mc" data-tip="FCF Yield|2.1% — moderate given market cap size."><div class="mc-label">FCF Yield</div><div class="mc-val">2.1%</div></div></div></div>
|
||
<div class="tab-pane" id="tab-analyst2"><p class="section-hd">Analyst & Market Data</p><div class="metrics-grid"><div class="mc" data-tip="Analyst Recommendation|Strong Buy consensus with 62 covering analysts — among the widest coverage in tech."><div class="mc-label">Recommendation</div><div class="mc-val pos">Strong Buy</div></div><div class="mc" data-tip="Analyst Count|62 covering analysts. Very high coverage depth = high signal confidence."><div class="mc-label"># Analysts</div><div class="mc-val">62</div></div><div class="mc" data-tip="Consensus Price Target|Median 12-month target. Implies 19.9% upside from current price."><div class="mc-label">Price Target</div><div class="mc-val">$1,050</div></div><div class="mc" data-tip="Upside to Target|+19.9% — analysts see meaningful upside despite YTD gains."><div class="mc-label">Upside to Target</div><div class="mc-val pos">+19.9%</div></div><div class="mc" data-tip="Short Interest|1.2% of float — minimal short positioning."><div class="mc-label">Short Interest</div><div class="mc-val muted">1.2%</div></div><div class="mc" data-tip="Beta|1.71 — significantly more volatile than the broader market."><div class="mc-label">Beta</div><div class="mc-val muted">1.71</div></div></div></div>
|
||
<div class="tab-pane chart-tab" id="tab-chart2">
|
||
<div class="chart-wrap">
|
||
<div class="chart-period-bar" id="cpbar2">
|
||
<div class="period-btn">1M</div><div class="period-btn">3M</div>
|
||
<div class="period-btn active">6M</div><div class="period-btn">1Y</div>
|
||
<div class="period-btn">2Y</div><div class="period-btn">5Y</div>
|
||
</div>
|
||
<canvas id="price-chart2"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="status-bar">
|
||
<span class="status-l">Search for any stock by ticker or name</span>
|
||
<span class="status-r">Cache: 4,200 stocks · updated 4 mins ago</span>
|
||
</div>
|
||
</div><!-- /search page -->
|
||
|
||
<!-- ══════════════ TRACKING PAGE ══════════════ -->
|
||
<div class="page" id="page-tracking">
|
||
<div class="tracking-subnav">
|
||
<button class="snav-btn active" onclick="setTrackingTab('insider',this)">Insider Trades</button>
|
||
<button class="snav-btn" onclick="setTrackingTab('hedgefund',this)">Hedge Funds</button>
|
||
</div>
|
||
|
||
<!-- INSIDER TRADES -->
|
||
<div class="tpage active" id="tpage-insider">
|
||
<div class="tracking-toolbar">
|
||
<span class="tr-label">Insider Transactions</span>
|
||
<div class="tr-sep"></div>
|
||
<div class="tperiod-btn">Today</div><div class="tperiod-btn active">1W</div>
|
||
<div class="tperiod-btn">2W</div><div class="tperiod-btn">1M</div>
|
||
<div class="tr-sep"></div>
|
||
<div class="tperiod-btn active">All</div><div class="tperiod-btn">Buys</div><div class="tperiod-btn">Sales</div>
|
||
<button class="btn-refresh">↻ Refresh</button>
|
||
<span class="tr-status">24 transactions loaded</span>
|
||
</div>
|
||
<div class="tracking-table-scroll">
|
||
<table class="tr-table">
|
||
<thead><tr>
|
||
<th>Date</th><th>Ticker</th><th>Company</th><th>Insider</th><th>Title</th>
|
||
<th>Type</th><th class="r">Shares</th><th class="r">Price</th><th class="r">Value</th><th class="r">After</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr><td class="dimmed">Mar 28</td><td style="font-weight:700">NVDA</td><td class="muted">Nvidia Corp</td><td>Jensen Huang</td><td class="muted">CEO</td><td><span class="type-sale">Sale</span></td><td class="r">120,000</td><td class="r">$878.40</td><td class="r">$105.4M</td><td class="r dimmed">887,432</td></tr>
|
||
<tr><td class="dimmed">Mar 27</td><td style="font-weight:700">MSFT</td><td class="muted">Microsoft Corp</td><td>Satya Nadella</td><td class="muted">CEO</td><td><span class="type-sale">Sale</span></td><td class="r">14,500</td><td class="r">$424.10</td><td class="r">$6.2M</td><td class="r dimmed">1,234,000</td></tr>
|
||
<tr><td class="dimmed">Mar 27</td><td style="font-weight:700">META</td><td class="muted">Meta Platforms</td><td>Mark Zuckerberg</td><td class="muted">CEO</td><td><span class="type-sale">Sale</span></td><td class="r">85,000</td><td class="r">$513.20</td><td class="r">$43.6M</td><td class="r dimmed">342,110,009</td></tr>
|
||
<tr><td class="dimmed">Mar 26</td><td style="font-weight:700">JPM</td><td class="muted">JPMorgan Chase</td><td>Daniel Pinto</td><td class="muted">President</td><td><span class="type-buy">Buy</span></td><td class="r">22,000</td><td class="r">$196.30</td><td class="r">$4.3M</td><td class="r dimmed">214,500</td></tr>
|
||
<tr><td class="dimmed">Mar 26</td><td style="font-weight:700">AMZN</td><td class="muted">Amazon.com</td><td>Andy Jassy</td><td class="muted">CEO</td><td><span class="type-award">Award</span></td><td class="r">62,500</td><td class="r">$183.22</td><td class="r">$11.5M</td><td class="r dimmed">1,840,000</td></tr>
|
||
<tr><td class="dimmed">Mar 25</td><td style="font-weight:700">GOOGL</td><td class="muted">Alphabet Inc</td><td>Sundar Pichai</td><td class="muted">CEO</td><td><span class="type-sale">Sale</span></td><td class="r">41,200</td><td class="r">$168.40</td><td class="r">$6.9M</td><td class="r dimmed">928,100</td></tr>
|
||
<tr><td class="dimmed">Mar 25</td><td style="font-weight:700">XOM</td><td class="muted">Exxon Mobil</td><td>Darren Woods</td><td class="muted">CEO</td><td><span class="type-buy">Buy</span></td><td class="r">15,000</td><td class="r">$112.80</td><td class="r">$1.7M</td><td class="r dimmed">473,200</td></tr>
|
||
<tr><td class="dimmed">Mar 24</td><td style="font-weight:700">UNH</td><td class="muted">UnitedHealth Group</div></td><td>Andrew Witty</td><td class="muted">CEO</td><td><span class="type-buy">Buy</span></td><td class="r">5,000</td><td class="r">$493.10</td><td class="r">$2.5M</td><td class="r dimmed">88,400</td></tr>
|
||
<tr><td class="dimmed">Mar 24</td><td style="font-weight:700">TSLA</td><td class="muted">Tesla Inc</td><td>Elon Musk</td><td class="muted">CEO</td><td><span class="type-award">Award</span></td><td class="r">304,000</td><td class="r">$174.50</td><td class="r">$53.1M</td><td class="r dimmed">411,062,512</td></tr>
|
||
<tr><td class="dimmed">Mar 23</td><td style="font-weight:700">AAPL</td><td class="muted">Apple Inc</td><td>Luca Maestri</td><td class="muted">CFO</td><td><span class="type-sale">Sale</span></td><td class="r">18,300</td><td class="r">$211.90</td><td class="r">$3.9M</td><td class="r dimmed">106,200</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="status-bar">
|
||
<span class="status-l">24 transactions · last 7 days · click any row to view SEC filing</span>
|
||
<span class="status-r">Data: SEC EDGAR Form 4</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- HEDGE FUNDS -->
|
||
<div class="tpage" id="tpage-hedgefund">
|
||
<div class="tracking-toolbar">
|
||
<span class="tr-label">Hedge Fund Holdings (13F-HR)</span>
|
||
<div class="tr-sep"></div>
|
||
<div class="tperiod-btn">30D</div><div class="tperiod-btn">60D</div>
|
||
<div class="tperiod-btn active">90D</div><div class="tperiod-btn">180D</div>
|
||
<div class="tr-sep"></div>
|
||
<div class="tperiod-btn active">All</div><div class="tperiod-btn">$1M+</div>
|
||
<div class="tperiod-btn">$10M+</div><div class="tperiod-btn">$100M+</div>
|
||
<button class="btn-refresh">↻ Refresh</button>
|
||
<span class="tr-status">156 holdings loaded</span>
|
||
</div>
|
||
<div class="tracking-table-scroll">
|
||
<table class="tr-table">
|
||
<thead><tr>
|
||
<th>Filed</th><th>Fund</th><th>Company</th>
|
||
<th class="r">Shares</th><th class="r">Value</th><th>Class</th><th>Opt</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr><td class="dimmed">Mar 15</td><td style="font-weight:600">Pershing Square Capital</td><td class="muted">Alphabet Inc (GOOGL)</td><td class="r">18,500,000</td><td class="r" style="color:var(--positive)">$3.11B</td><td class="dimmed">A</td><td class="dimmed">—</td></tr>
|
||
<tr><td class="dimmed">Mar 15</td><td style="font-weight:600">Bridgewater Associates</td><td class="muted">Apple Inc (AAPL)</td><td class="r">12,400,000</td><td class="r" style="color:var(--positive)">$2.65B</td><td class="dimmed">—</td><td class="dimmed">—</td></tr>
|
||
<tr><td class="dimmed">Mar 14</td><td style="font-weight:600">Tiger Global Mgmt</td><td class="muted">Microsoft Corp (MSFT)</td><td class="r">4,200,000</td><td class="r">$1.78B</td><td class="dimmed">—</td><td class="dimmed">—</td></tr>
|
||
<tr><td class="dimmed">Mar 14</td><td style="font-weight:600">Third Point LLC</td><td class="muted">Nvidia Corp (NVDA)</td><td class="r">1,850,000</td><td class="r">$1.62B</td><td class="dimmed">—</td><td class="dimmed">—</td></tr>
|
||
<tr><td class="dimmed">Mar 12</td><td style="font-weight:600">Renaissance Technologies</td><td class="muted">Meta Platforms (META)</td><td class="r">3,100,000</td><td class="r">$1.59B</td><td class="dimmed">A</td><td class="dimmed">—</td></tr>
|
||
<tr><td class="dimmed">Mar 12</td><td style="font-weight:600">Baupost Group</td><td class="muted">Amazon.com (AMZN)</td><td class="r">7,800,000</td><td class="r">$1.43B</td><td class="dimmed">—</td><td class="dimmed">Call</td></tr>
|
||
<tr><td class="dimmed">Mar 11</td><td style="font-weight:600">D.E. Shaw Group</td><td class="muted">Tesla Inc (TSLA)</td><td class="r">9,200,000</td><td class="r">$1.31B</td><td class="dimmed">—</td><td class="dimmed">—</td></tr>
|
||
<tr><td class="dimmed">Mar 10</td><td style="font-weight:600">Citadel Advisors</td><td class="muted">JPMorgan Chase (JPM)</td><td class="r">6,400,000</td><td class="r">$1.26B</td><td class="dimmed">—</td><td class="dimmed">—</td></tr>
|
||
<tr><td class="dimmed">Mar 10</td><td style="font-weight:600">Appaloosa Mgmt</td><td class="muted">Nvidia Corp (NVDA)</td><td class="r">1,100,000</td><td class="r">$964M</td><td class="dimmed">—</td><td class="dimmed">Put</td></tr>
|
||
<tr><td class="dimmed">Mar 8</td><td style="font-weight:600">Lone Pine Capital</td><td class="muted">UnitedHealth Group (UNH)</td><td class="r">1,920,000</td><td class="r">$947M</td><td class="dimmed">—</td><td class="dimmed">—</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="status-bar">
|
||
<span class="status-l">156 holdings · last 90 days · click any row to view SEC 13F filing</span>
|
||
<span class="status-r">Data: SEC EDGAR 13F-HR</span>
|
||
</div>
|
||
</div>
|
||
</div><!-- /tracking page -->
|
||
|
||
</div><!-- /main -->
|
||
</div><!-- /app -->
|
||
|
||
<script>
|
||
// ── Stock data ──────────────────────────────────────────────────────────────
|
||
const stocks = [
|
||
{ rank:1, ticker:'NVDA', name:'Nvidia Corp', score:91, sector:'Technology' },
|
||
{ rank:2, ticker:'AAPL', name:'Apple Inc', score:82, sector:'Technology' },
|
||
{ rank:3, ticker:'MSFT', name:'Microsoft Corp', score:79, sector:'Technology' },
|
||
{ rank:4, ticker:'META', name:'Meta Platforms', score:77, sector:'Comm. Services' },
|
||
{ rank:5, ticker:'GOOGL', name:'Alphabet Inc', score:74, sector:'Technology' },
|
||
{ rank:6, ticker:'AMZN', name:'Amazon.com', score:71, sector:'Cons. Discret.' },
|
||
{ rank:7, ticker:'JPM', name:'JPMorgan Chase', score:68, sector:'Financials' },
|
||
{ rank:8, ticker:'UNH', name:'UnitedHealth Group', score:65, sector:'Healthcare' },
|
||
{ rank:9, ticker:'LLY', name:'Eli Lilly & Co', score:63, sector:'Healthcare' },
|
||
{ rank:10, ticker:'V', name:'Visa Inc', score:61, sector:'Financials' },
|
||
{ rank:11, ticker:'XOM', name:'Exxon Mobil', score:59, sector:'Energy' },
|
||
{ rank:12, ticker:'JNJ', name:'Johnson & Johnson', score:57, sector:'Healthcare' },
|
||
{ rank:13, ticker:'WMT', name:'Walmart Inc', score:55, sector:'Cons. Staples' },
|
||
{ rank:14, ticker:'TSLA', name:'Tesla Inc', score:52, sector:'Cons. Discret.' },
|
||
{ rank:15, ticker:'PG', name:'Procter & Gamble', score:50, sector:'Cons. Staples' },
|
||
];
|
||
|
||
// ── Build stock table ───────────────────────────────────────────────────────
|
||
const tbody = document.getElementById('stock-tbody');
|
||
let selectedIdx = 1;
|
||
|
||
stocks.forEach((s, i) => {
|
||
const tr = document.createElement('tr');
|
||
if (i === selectedIdx) tr.classList.add('selected');
|
||
const cls = s.score >= 80 ? 'score-hi' : s.score >= 60 ? 'score-mid' : 'score-lo';
|
||
tr.innerHTML = `
|
||
<td class="num">${s.rank}</td>
|
||
<td class="ticker">${s.ticker}</td>
|
||
<td class="name">${s.name}</td>
|
||
<td class="score-cell ${cls}">${s.score}</td>
|
||
`;
|
||
tr.onclick = () => selectStock(i);
|
||
tbody.appendChild(tr);
|
||
});
|
||
|
||
function selectStock(idx) {
|
||
document.querySelectorAll('#stock-tbody tr').forEach((r,i) => r.classList.toggle('selected', i===idx));
|
||
const s = stocks[idx];
|
||
document.getElementById('detail-empty').style.display = 'none';
|
||
document.getElementById('detail-filled').style.display = 'flex';
|
||
document.getElementById('d-ticker').textContent = s.ticker;
|
||
document.getElementById('d-name').textContent = s.name;
|
||
document.getElementById('d-sector').textContent = '· ' + s.sector;
|
||
selectedIdx = idx;
|
||
// Reset to Scores tab
|
||
document.querySelectorAll('#detail-tabs .dtab').forEach((t,i) => t.classList.toggle('active', i===0));
|
||
document.querySelectorAll('#detail-body .tab-pane').forEach((p,i) => p.classList.toggle('active', i===0));
|
||
}
|
||
selectStock(selectedIdx);
|
||
|
||
// ── Page navigation ─────────────────────────────────────────────────────────
|
||
function setPage(name, el) {
|
||
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
||
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
||
const p = document.getElementById('page-'+name);
|
||
if (p) p.classList.add('active');
|
||
el.classList.add('active');
|
||
}
|
||
|
||
// ── Tab switching ───────────────────────────────────────────────────────────
|
||
function setTab(name, el, tabsId, bodyId) {
|
||
const tabsEl = document.getElementById(tabsId);
|
||
const bodyEl = document.getElementById(bodyId);
|
||
tabsEl.querySelectorAll('.dtab').forEach(t => t.classList.remove('active'));
|
||
bodyEl.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
|
||
el.classList.add('active');
|
||
const pane = document.getElementById('tab-'+name);
|
||
if (pane) {
|
||
pane.classList.add('active');
|
||
if (name === 'chart') { requestAnimationFrame(() => drawChart('price-chart', chartData['price-chart'])); }
|
||
if (name === 'chart2') { requestAnimationFrame(() => drawChart('price-chart2', chartData['price-chart2'])); }
|
||
}
|
||
}
|
||
|
||
// ── Tracking sub-nav ────────────────────────────────────────────────────────
|
||
function setTrackingTab(name, el) {
|
||
document.querySelectorAll('.tpage').forEach(p => p.classList.remove('active'));
|
||
document.querySelectorAll('.snav-btn').forEach(b => b.classList.remove('active'));
|
||
document.getElementById('tpage-'+name).classList.add('active');
|
||
el.classList.add('active');
|
||
}
|
||
|
||
// ── Search ──────────────────────────────────────────────────────────────────
|
||
function doSearch() {
|
||
document.getElementById('search-idle').style.display = 'none';
|
||
document.getElementById('search-result').style.display = 'flex';
|
||
}
|
||
|
||
// ── Chart drawing ───────────────────────────────────────────────────────────
|
||
function genChartData(days, start, end) {
|
||
const prices = [start], volumes = [];
|
||
const baseVol = start * 2e5;
|
||
for (let i = 1; i < days; i++) {
|
||
const trend = (end - start) / days;
|
||
const noise = (Math.random() - 0.45) * start * 0.025;
|
||
prices.push(Math.max(start * 0.7, prices[i-1] + trend + noise));
|
||
}
|
||
for (let i = 0; i < days; i++) {
|
||
volumes.push(Math.max(baseVol * 0.2, baseVol * (0.5 + Math.random())));
|
||
}
|
||
return { prices, volumes };
|
||
}
|
||
|
||
// X-axis month labels for 120-day range
|
||
const _MONTH_LABELS = ['Oct','Nov','Dec','Jan','Feb','Mar'];
|
||
|
||
function drawChart(id, data, hoverIdx = -1) {
|
||
const canvas = document.getElementById(id);
|
||
if (!canvas) return;
|
||
const rect = canvas.getBoundingClientRect();
|
||
const dpr = window.devicePixelRatio || 1;
|
||
const w = Math.round(rect.width) || canvas.parentElement.offsetWidth || 600;
|
||
const h = Math.round(rect.height) || canvas.parentElement.offsetHeight || 300;
|
||
canvas.width = w * dpr;
|
||
canvas.height = h * dpr;
|
||
canvas.style.width = w + 'px';
|
||
canvas.style.height = h + 'px';
|
||
const ctx = canvas.getContext('2d');
|
||
ctx.scale(dpr, dpr);
|
||
|
||
const { prices, volumes } = data;
|
||
const n = prices.length;
|
||
|
||
// Split canvas: 68% price, 32% volume
|
||
const pad = { top:20, right:20, bottom:32, left:56 };
|
||
const gap = 10; // gap between price and volume panels
|
||
const volH = Math.round((h - pad.top - pad.bottom - gap) * 0.30);
|
||
const priceH = h - pad.top - pad.bottom - gap - volH;
|
||
const cw = w - pad.left - pad.right;
|
||
|
||
ctx.clearRect(0,0,w,h);
|
||
ctx.fillStyle = '#181818'; ctx.fillRect(0,0,w,h);
|
||
|
||
const xOf = i => pad.left + (i / (n-1)) * cw;
|
||
|
||
// ── Price panel ────────────────────────────────
|
||
const pTop = pad.top;
|
||
const pBot = pad.top + priceH;
|
||
const mn = Math.min(...prices)*0.98, mx = Math.max(...prices)*1.02, rng = mx - mn;
|
||
const yP = v => pTop + (1 - (v - mn) / rng) * priceH;
|
||
|
||
// Price grid
|
||
for (let g = 0; g <= 4; g++) {
|
||
const y = pTop + (g/4)*priceH;
|
||
ctx.strokeStyle='#222'; ctx.lineWidth=1;
|
||
ctx.beginPath(); ctx.moveTo(pad.left,y); ctx.lineTo(w-pad.right,y); ctx.stroke();
|
||
ctx.fillStyle='#555'; ctx.font='10px Segoe UI,sans-serif'; ctx.textAlign='right';
|
||
ctx.fillText('$'+(mx-(g/4)*rng).toFixed(0), pad.left-6, y+4);
|
||
}
|
||
|
||
// Area fill
|
||
const grad = ctx.createLinearGradient(0,pTop,0,pBot);
|
||
grad.addColorStop(0,'rgba(255,255,255,0.07)'); grad.addColorStop(1,'rgba(255,255,255,0)');
|
||
ctx.beginPath(); ctx.moveTo(xOf(0),yP(prices[0]));
|
||
prices.forEach((v,i)=>{ if(i>0) ctx.lineTo(xOf(i),yP(v)); });
|
||
ctx.lineTo(xOf(n-1),pBot); ctx.lineTo(xOf(0),pBot);
|
||
ctx.closePath(); ctx.fillStyle=grad; ctx.fill();
|
||
|
||
// Price line
|
||
ctx.beginPath(); ctx.moveTo(xOf(0),yP(prices[0]));
|
||
prices.forEach((v,i)=>{ if(i>0) ctx.lineTo(xOf(i),yP(v)); });
|
||
ctx.strokeStyle='#fff'; ctx.lineWidth=1.5; ctx.stroke();
|
||
|
||
// Endpoint dot
|
||
ctx.beginPath(); ctx.arc(xOf(n-1),yP(prices[n-1]),4,0,Math.PI*2);
|
||
ctx.fillStyle='#fff'; ctx.fill();
|
||
|
||
// ── Volume panel ───────────────────────────────
|
||
const vTop = pBot + gap;
|
||
const vBot = vTop + volH;
|
||
const maxVol = Math.max(...volumes);
|
||
const barW = Math.max(1, cw / n - 1);
|
||
|
||
volumes.forEach((v,i) => {
|
||
const bh = (v / maxVol) * volH;
|
||
const bx = pad.left + (i/(n-1))*cw - barW/2;
|
||
const isUp = i === 0 || prices[i] >= prices[i-1];
|
||
ctx.fillStyle = isUp ? 'rgba(74,222,128,0.55)' : 'rgba(248,113,113,0.55)';
|
||
ctx.fillRect(bx, vBot - bh, barW, bh);
|
||
});
|
||
|
||
// Volume baseline
|
||
ctx.strokeStyle='#333'; ctx.lineWidth=1;
|
||
ctx.beginPath(); ctx.moveTo(pad.left,vBot); ctx.lineTo(w-pad.right,vBot); ctx.stroke();
|
||
|
||
// ── X labels ─────────────────────────────────
|
||
_MONTH_LABELS.forEach((l,i,a) => {
|
||
ctx.fillStyle='#555'; ctx.font='10px Segoe UI,sans-serif'; ctx.textAlign='center';
|
||
ctx.fillText(l, pad.left+(i/(a.length-1))*cw, h - 8);
|
||
});
|
||
|
||
// ── Hover crosshair ───────────────────────────
|
||
if (hoverIdx >= 0 && hoverIdx < n) {
|
||
const hx = xOf(hoverIdx);
|
||
// Vertical line spanning both panels
|
||
ctx.setLineDash([3,3]);
|
||
ctx.strokeStyle = 'rgba(255,255,255,0.25)'; ctx.lineWidth = 1;
|
||
ctx.beginPath(); ctx.moveTo(hx, pTop); ctx.lineTo(hx, vBot); ctx.stroke();
|
||
ctx.setLineDash([]);
|
||
// Price dot
|
||
const hy = yP(prices[hoverIdx]);
|
||
ctx.beginPath(); ctx.arc(hx, hy, 5, 0, Math.PI*2);
|
||
ctx.fillStyle='#fff'; ctx.fill();
|
||
ctx.beginPath(); ctx.arc(hx, hy, 3, 0, Math.PI*2);
|
||
ctx.fillStyle='#181818'; ctx.fill();
|
||
}
|
||
}
|
||
|
||
// ── Chart hover interaction ─────────────────────────────────────────────────
|
||
const _chartTip = document.getElementById('chart-tip');
|
||
const _ctipDate = document.getElementById('ctip-date');
|
||
const _ctipPrice = document.getElementById('ctip-price');
|
||
const _ctipChg = document.getElementById('ctip-chg');
|
||
const _ctipVol = document.getElementById('ctip-vol');
|
||
|
||
function _fmtVol(v) {
|
||
if (v >= 1e9) return (v/1e9).toFixed(2)+'B';
|
||
if (v >= 1e6) return (v/1e6).toFixed(1)+'M';
|
||
if (v >= 1e3) return (v/1e3).toFixed(0)+'K';
|
||
return v.toFixed(0);
|
||
}
|
||
|
||
function setupChartHover(canvasId, data) {
|
||
const canvas = document.getElementById(canvasId);
|
||
if (!canvas) return;
|
||
const { prices, volumes } = data;
|
||
const n = prices.length;
|
||
|
||
canvas.addEventListener('mousemove', e => {
|
||
const rect = canvas.getBoundingClientRect();
|
||
const mx = e.clientX - rect.left;
|
||
const pad = { left: 56, right: 20 };
|
||
const cw = rect.width - pad.left - pad.right;
|
||
const frac = (mx - pad.left) / cw;
|
||
const idx = Math.min(n-1, Math.max(0, Math.round(frac * (n-1))));
|
||
|
||
drawChart(canvasId, data, idx);
|
||
|
||
// Compute date label (fake: 120 days ending today)
|
||
const msPerDay = 86400000;
|
||
const endDate = new Date(); endDate.setHours(0,0,0,0);
|
||
const d = new Date(endDate.getTime() - (n - 1 - idx) * msPerDay);
|
||
const dateStr = d.toLocaleDateString('en-US', { month:'short', day:'numeric', year:'numeric' });
|
||
|
||
const pct = ((prices[idx] - prices[0]) / prices[0] * 100);
|
||
const pctStr = (pct >= 0 ? '+' : '') + pct.toFixed(2) + '%';
|
||
|
||
_ctipDate.textContent = dateStr;
|
||
_ctipPrice.textContent = '$' + prices[idx].toFixed(2);
|
||
_ctipChg.textContent = pctStr;
|
||
_ctipChg.style.color = pct >= 0 ? '#4ade80' : '#f87171';
|
||
_ctipVol.textContent = 'Vol ' + _fmtVol(volumes[idx]);
|
||
|
||
// Position tip
|
||
const vw = window.innerWidth, vh = window.innerHeight;
|
||
let tx = e.clientX + 16, ty = e.clientY - 60;
|
||
if (tx + 170 > vw) tx = e.clientX - 170;
|
||
if (ty < 8) ty = 8;
|
||
if (ty + 100 > vh) ty = vh - 108;
|
||
_chartTip.style.left = tx + 'px';
|
||
_chartTip.style.top = ty + 'px';
|
||
_chartTip.classList.add('visible');
|
||
});
|
||
|
||
canvas.addEventListener('mouseleave', () => {
|
||
_chartTip.classList.remove('visible');
|
||
drawChart(canvasId, data);
|
||
});
|
||
}
|
||
|
||
// ── Period button interaction ───────────────────────────────────────────────
|
||
document.querySelectorAll('.chart-period-bar').forEach(bar => {
|
||
bar.querySelectorAll('.period-btn').forEach(btn => {
|
||
btn.onclick = () => {
|
||
bar.querySelectorAll('.period-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
};
|
||
});
|
||
});
|
||
|
||
// ── Resize observer — redraw charts whenever container changes size ──────────
|
||
const chartData = {
|
||
'price-chart': genChartData(120, 170, 213),
|
||
'price-chart2': genChartData(120, 450, 875),
|
||
};
|
||
const ro = new ResizeObserver(() => {
|
||
['price-chart', 'price-chart2'].forEach(id => {
|
||
const c = document.getElementById(id);
|
||
if (c && c.closest('.chart-tab.active')) drawChart(id, chartData[id]);
|
||
});
|
||
});
|
||
document.querySelectorAll('.chart-tab').forEach(el => ro.observe(el));
|
||
|
||
// Set up hover on both charts
|
||
setupChartHover('price-chart', chartData['price-chart']);
|
||
setupChartHover('price-chart2', chartData['price-chart2']);
|
||
|
||
// ── Tooltip system ──────────────────────────────────────────────────────────
|
||
const tip = document.getElementById('tooltip');
|
||
const tipTitle = document.getElementById('tip-title');
|
||
const tipDesc = document.getElementById('tip-desc');
|
||
const tipCtx = document.getElementById('tip-context');
|
||
|
||
let tipTimeout;
|
||
|
||
document.addEventListener('mouseover', e => {
|
||
const cell = e.target.closest('.mc[data-tip]');
|
||
if (!cell) return;
|
||
|
||
const parts = cell.getAttribute('data-tip').split('|');
|
||
tipTitle.textContent = parts[0] || '';
|
||
tipDesc.textContent = parts[1] || '';
|
||
tipCtx.innerHTML = parts[2] || '';
|
||
|
||
clearTimeout(tipTimeout);
|
||
tip.classList.add('visible');
|
||
});
|
||
|
||
document.addEventListener('mousemove', e => {
|
||
if (!tip.classList.contains('visible')) return;
|
||
const vw = window.innerWidth, vh = window.innerHeight;
|
||
const tw = tip.offsetWidth + 16, th = tip.offsetHeight + 16;
|
||
let x = e.clientX + 14, y = e.clientY - 10;
|
||
if (x + tw > vw) x = e.clientX - tw;
|
||
if (y + th > vh) y = e.clientY - th;
|
||
if (y < 0) y = 8;
|
||
tip.style.left = x + 'px';
|
||
tip.style.top = y + 'px';
|
||
});
|
||
|
||
document.addEventListener('mouseout', e => {
|
||
if (!e.target.closest('.mc[data-tip]')) return;
|
||
tipTimeout = setTimeout(() => tip.classList.remove('visible'), 80);
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|