589 lines
19 KiB
HTML
589 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||
<title>Portfolio — Samantha Friis</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Syne:wght@400;700;800&display=swap" rel="stylesheet"/>
|
||
<link rel="stylesheet" href="styles.css">
|
||
<style>
|
||
/* ── Trades accordion ── */
|
||
.trades-section {
|
||
margin-top: 2.5rem;
|
||
}
|
||
|
||
.trades-toggle {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
cursor: pointer;
|
||
background: none;
|
||
border: none;
|
||
border-top: 1px solid var(--border, #2a2a3a);
|
||
border-bottom: 1px solid var(--border, #2a2a3a);
|
||
width: 100%;
|
||
padding: 0.85rem 0;
|
||
color: inherit;
|
||
font-family: 'DM Mono', monospace;
|
||
font-size: 0.75rem;
|
||
letter-spacing: 0.07em;
|
||
text-align: left;
|
||
user-select: none;
|
||
}
|
||
|
||
.trades-toggle:hover .toggle-label {
|
||
color: var(--accent, #f59e0b);
|
||
}
|
||
|
||
.toggle-label {
|
||
color: var(--muted, #475569);
|
||
transition: color 0.15s;
|
||
}
|
||
|
||
.toggle-label span {
|
||
color: var(--accent, #f59e0b);
|
||
}
|
||
|
||
.toggle-icon {
|
||
color: var(--muted, #475569);
|
||
font-size: 1rem;
|
||
transition: transform 0.25s ease;
|
||
line-height: 1;
|
||
}
|
||
|
||
.trades-toggle[aria-expanded="true"] .toggle-icon {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
.trades-body {
|
||
display: grid;
|
||
grid-template-rows: 0fr;
|
||
transition: grid-template-rows 0.3s ease;
|
||
}
|
||
|
||
.trades-body.open {
|
||
grid-template-rows: 1fr;
|
||
}
|
||
|
||
.trades-inner {
|
||
overflow: hidden;
|
||
}
|
||
|
||
.trades-filter {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
flex-wrap: wrap;
|
||
padding: 1rem 0 0.75rem;
|
||
}
|
||
|
||
.filter-btn {
|
||
background: none;
|
||
border: 1px solid var(--border, #2a2a3a);
|
||
border-radius: 4px;
|
||
color: var(--muted, #475569);
|
||
font-family: 'DM Mono', monospace;
|
||
font-size: 0.7rem;
|
||
letter-spacing: 0.06em;
|
||
padding: 3px 10px;
|
||
cursor: pointer;
|
||
transition: all 0.15s;
|
||
}
|
||
|
||
.filter-btn:hover,
|
||
.filter-btn.active {
|
||
border-color: var(--accent, #f59e0b);
|
||
color: var(--accent, #f59e0b);
|
||
background: rgba(245,158,11,0.06);
|
||
}
|
||
|
||
.trades-table-wrap {
|
||
overflow-x: auto;
|
||
padding-bottom: 1.5rem;
|
||
}
|
||
|
||
.trades-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.78rem;
|
||
}
|
||
|
||
.trades-table thead tr {
|
||
border-bottom: 1px solid var(--border, #2a2a3a);
|
||
}
|
||
|
||
.trades-table th {
|
||
color: var(--muted, #475569);
|
||
font-weight: 400;
|
||
letter-spacing: 0.07em;
|
||
font-size: 0.68rem;
|
||
padding: 0.5rem 0.75rem;
|
||
text-align: left;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.trades-table td {
|
||
padding: 0.65rem 0.75rem;
|
||
border-bottom: 1px solid rgba(42,42,58,0.5);
|
||
white-space: nowrap;
|
||
color: var(--text, #e2e8f0);
|
||
}
|
||
|
||
.trades-table tbody tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.trades-table tbody tr:hover td {
|
||
background: rgba(255,255,255,0.02);
|
||
}
|
||
|
||
.trades-table tr.hidden-row {
|
||
display: none;
|
||
}
|
||
|
||
.dir-buy { color: #4ade80; }
|
||
.dir-sell { color: #f87171; }
|
||
|
||
.pnl-pos { color: #4ade80; }
|
||
.pnl-neg { color: #f87171; }
|
||
.pnl-zero{ color: var(--muted, #475569); }
|
||
|
||
.trade-code {
|
||
display: inline-block;
|
||
padding: 1px 6px;
|
||
border-radius: 3px;
|
||
font-size: 0.65rem;
|
||
background: rgba(42,42,58,0.8);
|
||
color: var(--muted, #475569);
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.trade-ticker {
|
||
font-weight: 500;
|
||
color: #f1f5f9;
|
||
}
|
||
|
||
.trades-count {
|
||
color: var(--muted, #475569);
|
||
font-size: 0.68rem;
|
||
padding: 0 0 0.5rem;
|
||
}
|
||
|
||
/* ── Live price styles ── */
|
||
.price-cell {
|
||
position: relative;
|
||
}
|
||
.price-val {
|
||
transition: color 0.4s;
|
||
}
|
||
.price-val.loading {
|
||
color: var(--muted, #475569);
|
||
}
|
||
.price-val.flash-up {
|
||
color: #4ade80;
|
||
}
|
||
.price-val.flash-down {
|
||
color: #f87171;
|
||
}
|
||
.price-status {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.4rem;
|
||
font-size: 0.68rem;
|
||
color: var(--muted, #475569);
|
||
margin-left: 0.5rem;
|
||
vertical-align: middle;
|
||
}
|
||
.price-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--muted, #475569);
|
||
display: inline-block;
|
||
}
|
||
.price-dot.live { background: #4ade80; box-shadow: 0 0 4px #4ade80; }
|
||
.price-dot.error { background: #f87171; }
|
||
.price-dot.loading {
|
||
background: #f59e0b;
|
||
animation: pulse 1s ease-in-out infinite;
|
||
}
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.3; }
|
||
}
|
||
.refresh-btn {
|
||
background: none;
|
||
border: 1px solid var(--border, #2a2a3a);
|
||
border-radius: 4px;
|
||
color: var(--muted, #475569);
|
||
font-family: 'DM Mono', monospace;
|
||
font-size: 0.68rem;
|
||
padding: 2px 8px;
|
||
cursor: pointer;
|
||
letter-spacing: 0.05em;
|
||
transition: all 0.15s;
|
||
}
|
||
.refresh-btn:hover {
|
||
border-color: var(--accent, #f59e0b);
|
||
color: var(--accent, #f59e0b);
|
||
}
|
||
.section-header-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
margin-bottom: 0.6rem;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
|
||
<header>
|
||
<a href="/" class="back">← back</a>
|
||
<p class="tag">// portfolio</p>
|
||
<h1>Investment<br/><span>Portfolio</span></h1>
|
||
<p class="header-sub">Equity positions · Personal research</p>
|
||
</header>
|
||
|
||
<!-- Summary cards -->
|
||
<div class="cards">
|
||
<div class="card">
|
||
<p class="card-label">Total Weight</p>
|
||
<p class="card-value neutral">100,10%</p>
|
||
</div>
|
||
<div class="card">
|
||
<p class="card-label">Weighted Return</p>
|
||
<p class="card-value up">+7,84%</p>
|
||
</div>
|
||
<div class="card">
|
||
<p class="card-label">Price Return (w)</p>
|
||
<p class="card-value up">+7,73%</p>
|
||
</div>
|
||
<div class="card">
|
||
<p class="card-label">Positions</p>
|
||
<p class="card-value neutral">5</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Invested table -->
|
||
<div class="section-header-row">
|
||
<p class="section-label" style="margin:0">// invested positions</p>
|
||
<span class="price-status" id="priceStatus">
|
||
<span class="price-dot loading" id="priceDot"></span>
|
||
<span id="priceStatusText">fetching prices…</span>
|
||
</span>
|
||
<button class="refresh-btn" onclick="fetchHoldingPrices()">↺ refresh</button>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Ticker</th>
|
||
<th>Market Price</th>
|
||
<th>Avg Price</th>
|
||
<th>Shares Out.</th>
|
||
<th>Mkt Cap (M)</th>
|
||
<th>Weight</th>
|
||
<th>Return Rate</th>
|
||
<th>Price Return</th>
|
||
<th>Rate × Weight</th>
|
||
<th>P.Return × Weight</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr data-ticker="MAERSK-B.CO">
|
||
<td><span class="ticker">MEARSK</span><span class="currency-badge">DKK</span></td>
|
||
<td class="price-cell"><span class="price-val" id="price-MAERSK-B.CO">15,270.00</span></td><td>12,414</td><td>15,224,309</td><td>232,475</td>
|
||
<td><div class="weight-cell">45.95%<div class="weight-bar-track"><div class="weight-bar-fill" style="width:46%"></div></div></div></td>
|
||
<td class="neutral">9%</td><td class="up">23.01%</td><td>4.17%</td><td class="up">10.57%</td>
|
||
</tr>
|
||
<tr data-ticker="DFDS.CO">
|
||
<td><span class="ticker">DFDS</span><span class="currency-badge">DKK</span></td>
|
||
<td class="price-cell"><span class="price-val" id="price-DFDS.CO">98.80</span></td><td>89.82</td><td>53,892,216</td><td>5,325</td>
|
||
<td><div class="weight-cell">10.43%<div class="weight-bar-track"><div class="weight-bar-fill" style="width:10%"></div></div></div></td>
|
||
<td class="neutral">8%</td><td class="up">10.00%</td><td>0.83%</td><td class="up">1.04%</td>
|
||
</tr>
|
||
<tr data-ticker="ORSTED.CO">
|
||
<td><span class="ticker">ORSTED</span><span class="currency-badge">DKK</span></td>
|
||
<td class="price-cell"><span class="price-val" id="price-ORSTED.CO">152.00</span></td><td>128.80</td><td>756,088,069</td><td>114,925</td>
|
||
<td><div class="weight-cell">9.17%<div class="weight-bar-track"><div class="weight-bar-fill" style="width:9%"></div></div></div></td>
|
||
<td class="neutral">3.25%</td><td class="up">18.01%</td><td>0.30%</td><td class="up">1.65%</td>
|
||
</tr>
|
||
<tr data-ticker="CSIQ">
|
||
<td><span class="ticker">CSIQ</span><span class="currency-badge">USD</span></td>
|
||
<td class="price-cell"><span class="price-val" id="price-CSIQ">20.74</span></td><td>20.53</td><td>67,620,463</td><td>1,402</td>
|
||
<td><div class="weight-cell">3.09%<div class="weight-bar-track"><div class="weight-bar-fill" style="width:3%"></div></div></div></td>
|
||
<td class="neutral">4.95%</td><td class="up">1.02%</td><td>0.15%</td><td class="up">0.03%</td>
|
||
</tr>
|
||
<tr data-ticker="NOVO-B.CO">
|
||
<td><span class="ticker">NOVO NORDISK</span><span class="currency-badge">DKK</span></td>
|
||
<td class="price-cell"><span class="price-val" id="price-NOVO-B.CO">251.00</span></td><td>305.00</td><td>4,442,064,180</td><td>1,114,958</td>
|
||
<td><div class="weight-cell">31.46%<div class="weight-bar-track"><div class="weight-bar-fill" style="width:31%"></div></div></div></td>
|
||
<td class="neutral">8%</td><td class="down">−17.70%</td><td>2.38%</td><td class="down">−5.57%</td>
|
||
</tr>
|
||
</tbody>
|
||
<tbody>
|
||
<tr><td colspan="10" class="watchlist-label">// watchlist — not invested</td></tr>
|
||
<tr class="watchlist-row">
|
||
<td><span class="ticker" style="color:var(--muted)">COST</span><span class="currency-badge">USD</span></td>
|
||
<td>978.00</td><td>—</td><td>—</td><td>—</td>
|
||
<td><div class="weight-cell">—</div></td>
|
||
<td>—</td><td>—</td><td>—</td><td>—</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- ── Trades accordion ── -->
|
||
<div class="trades-section">
|
||
<button class="trades-toggle" aria-expanded="false" onclick="toggleTrades(this)">
|
||
<span class="toggle-label">// <span>trade history</span> — all executed orders</span>
|
||
<span class="toggle-icon">▾</span>
|
||
</button>
|
||
|
||
<div class="trades-body" id="tradesBody">
|
||
<div class="trades-inner">
|
||
|
||
<!-- Filter buttons -->
|
||
<div class="trades-filter">
|
||
<button class="filter-btn active" onclick="filterTrades('all', this)">All</button>
|
||
<button class="filter-btn" onclick="filterTrades('NOVOBc', this)">NOVOBc</button>
|
||
<button class="filter-btn" onclick="filterTrades('CSIQ', this)">CSIQ</button>
|
||
<button class="filter-btn" onclick="filterTrades('PYPL', this)">PYPL</button>
|
||
<button class="filter-btn" onclick="filterTrades('options', this)">Options</button>
|
||
</div>
|
||
|
||
<p class="trades-count" id="tradesCount">10 trades</p>
|
||
|
||
<div class="trades-table-wrap">
|
||
<table class="trades-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Symbol</th>
|
||
<th>Asset</th>
|
||
<th>Ccy</th>
|
||
<th>Date / Time</th>
|
||
<th>Dir</th>
|
||
<th>Qty</th>
|
||
<th>Trade Price</th>
|
||
<th>Curr. Price</th>
|
||
<th>Proceeds</th>
|
||
<th>Comm/Fee</th>
|
||
<th>Basis</th>
|
||
<th>Realized P/L</th>
|
||
<th>MTM P/L</th>
|
||
<th>Code</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tradesBody_rows">
|
||
|
||
<!-- NOVOBc -->
|
||
<tr data-sym="NOVOBc">
|
||
<td><span class="trade-ticker">NOVOBc</span></td>
|
||
<td>Stocks</td><td>DKK</td>
|
||
<td>2026-02-24 03:05</td>
|
||
<td class="dir-buy">BUY</td>
|
||
<td>8</td><td>241.70</td><td>243.65</td>
|
||
<td>−1,933.60</td><td>−10.00</td><td>1,943.60</td>
|
||
<td class="pnl-zero">0.00</td><td class="pnl-pos">+15.60</td>
|
||
<td><span class="trade-code">O</span></td>
|
||
</tr>
|
||
<tr data-sym="NOVOBc">
|
||
<td><span class="trade-ticker">NOVOBc</span></td>
|
||
<td>Stocks</td><td>DKK</td>
|
||
<td>2026-02-25 07:10</td>
|
||
<td class="dir-buy">BUY</td>
|
||
<td>3</td><td>239.50</td><td>238.40</td>
|
||
<td>−718.50</td><td>−10.00</td><td>728.50</td>
|
||
<td class="pnl-zero">0.00</td><td class="pnl-neg">−3.30</td>
|
||
<td><span class="trade-code">O</span></td>
|
||
</tr>
|
||
|
||
<!-- CSIQ -->
|
||
<tr data-sym="CSIQ">
|
||
<td><span class="trade-ticker">CSIQ</span></td>
|
||
<td>Stocks</td><td>USD</td>
|
||
<td>2026-01-20 14:33</td>
|
||
<td class="dir-buy">BUY</td>
|
||
<td>8</td><td>20.48</td><td>20.51</td>
|
||
<td>−163.84</td><td>−0.38</td><td>164.22</td>
|
||
<td class="pnl-zero">0.00</td><td class="pnl-pos">+0.24</td>
|
||
<td><span class="trade-code">O</span></td>
|
||
</tr>
|
||
|
||
<!-- PYPL -->
|
||
<tr data-sym="PYPL">
|
||
<td><span class="trade-ticker">PYPL</span></td>
|
||
<td>Stocks</td><td>USD</td>
|
||
<td>2026-01-20 14:31</td>
|
||
<td class="dir-sell">SELL</td>
|
||
<td>−3</td><td>55.18</td><td>55.08</td>
|
||
<td>165.53</td><td>−0.35</td><td>−187.91</td>
|
||
<td class="pnl-neg">−22.73</td><td class="pnl-pos">+0.29</td>
|
||
<td><span class="trade-code">C</span></td>
|
||
</tr>
|
||
|
||
<!-- Options -->
|
||
<tr data-sym="options">
|
||
<td><span class="trade-ticker">RGTI 15JAN27 5.5 P</span></td>
|
||
<td>Options</td><td>USD</td>
|
||
<td>2026-02-24 09:34</td>
|
||
<td class="dir-buy">BUY</td>
|
||
<td>1</td><td>0.53</td><td>0.5271</td>
|
||
<td>−53.00</td><td>−1.05</td><td>54.05</td>
|
||
<td class="pnl-zero">0.00</td><td class="pnl-neg">−0.29</td>
|
||
<td><span class="trade-code">O</span></td>
|
||
</tr>
|
||
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<footer>
|
||
<a href="mailto:contact@samantha42.xyz">contact@samantha42.xyz</a>
|
||
<p class="footer-copy">© 2026 — All rights reserved</p>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
// ── Holdings price fetch ──
|
||
// Calls the Go backend's /api/prices endpoint with a list of tickers.
|
||
// Falls back to FetchEOD-style Yahoo Finance if the backend isn't available.
|
||
|
||
const HOLDINGS = [
|
||
{ ticker: 'MAERSK-B.CO', label: 'MEARSK' },
|
||
{ ticker: 'DFDS.CO', label: 'DFDS' },
|
||
{ ticker: 'ORSTED.CO', label: 'ORSTED' },
|
||
{ ticker: 'CSIQ', label: 'CSIQ' },
|
||
{ ticker: 'NOVO-B.CO', label: 'NOVO NORDISK' },
|
||
];
|
||
|
||
function setStatus(state, text) {
|
||
const dot = document.getElementById('priceDot');
|
||
const label = document.getElementById('priceStatusText');
|
||
dot.className = 'price-dot ' + state;
|
||
label.textContent = text;
|
||
}
|
||
|
||
function flashPrice(id, newVal, oldVal) {
|
||
const el = document.getElementById('price-' + id);
|
||
if (!el) return;
|
||
const direction = newVal > oldVal ? 'flash-up' : newVal < oldVal ? 'flash-down' : '';
|
||
el.textContent = newVal.toLocaleString('da-DK', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
if (direction) {
|
||
el.classList.add(direction);
|
||
setTimeout(() => el.classList.remove(direction), 1200);
|
||
}
|
||
}
|
||
|
||
async function fetchHoldingPrices() {
|
||
setStatus('loading', 'fetching prices…');
|
||
|
||
// Read current displayed values before overwriting
|
||
const prev = {};
|
||
HOLDINGS.forEach(h => {
|
||
const el = document.getElementById('price-' + h.ticker);
|
||
if (el) prev[h.ticker] = parseFloat(el.textContent.replace(/\./g, '').replace(',', '.')) || 0;
|
||
});
|
||
|
||
try {
|
||
// POST to Go backend — expects JSON array: [{ticker, close}, ...]
|
||
const res = await fetch('/api/prices', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ tickers: HOLDINGS.map(h => h.ticker) }),
|
||
});
|
||
|
||
if (!res.ok) throw new Error('backend returned ' + res.status);
|
||
|
||
const data = await res.json(); // [{ ticker, close, date, currency }, ...]
|
||
const map = {};
|
||
data.forEach(d => { map[d.ticker] = d.close; });
|
||
|
||
HOLDINGS.forEach(h => {
|
||
const close = map[h.ticker];
|
||
if (close != null) flashPrice(h.ticker, close, prev[h.ticker] || close);
|
||
});
|
||
|
||
const now = new Date().toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
|
||
setStatus('live', 'updated ' + now);
|
||
|
||
} catch (err) {
|
||
console.warn('Backend unavailable, trying Yahoo Finance directly…', err);
|
||
await fetchDirectYahoo(prev);
|
||
}
|
||
}
|
||
|
||
// Direct Yahoo Finance fallback (CORS proxy) — same approach as the React artifact
|
||
async function fetchDirectYahoo(prev) {
|
||
const yesterday = new Date();
|
||
yesterday.setDate(yesterday.getDate() - 1);
|
||
// Roll back past weekends
|
||
while (yesterday.getDay() === 0 || yesterday.getDay() === 6) {
|
||
yesterday.setDate(yesterday.getDate() - 1);
|
||
}
|
||
const start = Math.floor(yesterday.setHours(0,0,0,0) / 1000);
|
||
const end = start + 86400;
|
||
|
||
let ok = 0, fail = 0;
|
||
|
||
await Promise.allSettled(HOLDINGS.map(async h => {
|
||
try {
|
||
const url = `https://corsproxy.io/?${encodeURIComponent(
|
||
`https://query1.finance.yahoo.com/v8/finance/chart/${h.ticker}?period1=${start}&period2=${end}&interval=1d`
|
||
)}`;
|
||
const res = await fetch(url);
|
||
const json = await res.json();
|
||
const close = json?.chart?.result?.[0]?.indicators?.quote?.[0]?.close?.[0];
|
||
if (close) {
|
||
flashPrice(h.ticker, close, prev[h.ticker] || close);
|
||
ok++;
|
||
} else {
|
||
fail++;
|
||
}
|
||
} catch { fail++; }
|
||
}));
|
||
|
||
const now = new Date().toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
|
||
if (fail === 0) {
|
||
setStatus('live', 'updated ' + now + ' (direct)');
|
||
} else if (ok > 0) {
|
||
setStatus('live', `${ok} ok, ${fail} failed — ${now}`);
|
||
} else {
|
||
setStatus('error', 'price fetch failed');
|
||
}
|
||
}
|
||
|
||
// Auto-fetch on page load
|
||
document.addEventListener('DOMContentLoaded', fetchHoldingPrices);
|
||
|
||
// ── Trades accordion ──
|
||
function toggleTrades(btn) {
|
||
const body = document.getElementById('tradesBody');
|
||
const expanded = btn.getAttribute('aria-expanded') === 'true';
|
||
btn.setAttribute('aria-expanded', String(!expanded));
|
||
body.classList.toggle('open', !expanded);
|
||
}
|
||
|
||
function filterTrades(sym, btn) {
|
||
// Update active button
|
||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
|
||
const rows = document.querySelectorAll('#tradesBody_rows tr');
|
||
let visible = 0;
|
||
rows.forEach(row => {
|
||
const match = sym === 'all' || row.dataset.sym === sym;
|
||
row.classList.toggle('hidden-row', !match);
|
||
if (match) visible++;
|
||
});
|
||
|
||
document.getElementById('tradesCount').textContent =
|
||
visible + ' trade' + (visible !== 1 ? 's' : '') + (sym !== 'all' ? ' — ' + sym : '');
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |