AndyKandy26 commited on
Commit
fd453c6
·
verified ·
1 Parent(s): b2e988f

Delete tracker.html

Browse files
Files changed (1) hide show
  1. tracker.html +0 -551
tracker.html DELETED
@@ -1,551 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>FinWise — Investment Tracker</title>
7
- <link rel="stylesheet" href="shared.css">
8
- <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
9
- <style>
10
- .tracker-header-bar {
11
- display: flex;
12
- align-items: center;
13
- justify-content: space-between;
14
- flex-wrap: wrap;
15
- gap: 12px;
16
- margin-bottom: 20px;
17
- }
18
- .holding-row td:first-child { font-weight: 700; }
19
- .gain-cell { font-family: var(--font-mono); font-weight: 700; }
20
- .ticker-cell {
21
- display: flex;
22
- align-items: center;
23
- gap: 10px;
24
- }
25
- .ticker-logo {
26
- width: 34px; height: 34px;
27
- border-radius: 8px;
28
- display: flex; align-items: center; justify-content: center;
29
- font-size: 9px;
30
- font-weight: 800;
31
- font-family: var(--font-mono);
32
- color: var(--bg);
33
- flex-shrink: 0;
34
- }
35
- .ticker-name-sub { font-size: 11px; color: var(--text2); font-weight: 400; }
36
- .mini-chart { width: 80px; height: 34px; }
37
- .pnl-bar-mini { height: 3px; border-radius: 2px; margin-top: 4px; }
38
-
39
- .edit-input {
40
- background: transparent;
41
- border: none;
42
- border-bottom: 1px dashed var(--border2);
43
- color: var(--text);
44
- font-family: var(--font-mono);
45
- font-size: 13px;
46
- width: 80px;
47
- padding: 2px 4px;
48
- text-align: right;
49
- outline: none;
50
- }
51
- .edit-input:focus { border-bottom-color: var(--cyan); }
52
-
53
- .add-holding-form {
54
- display: grid;
55
- grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
56
- gap: 12px;
57
- padding: 16px;
58
- background: var(--bg3);
59
- border-radius: var(--r-sm);
60
- border: 1px dashed var(--border2);
61
- margin-top: 16px;
62
- }
63
-
64
- .summary-strip {
65
- display: grid;
66
- grid-template-columns: repeat(auto-fit, minmax(140px,1fr));
67
- gap: 12px;
68
- margin-bottom: 20px;
69
- }
70
- .ss-item {
71
- background: var(--card);
72
- border: 1px solid var(--border);
73
- border-radius: var(--r-sm);
74
- padding: 14px 16px;
75
- display: flex;
76
- flex-direction: column;
77
- gap: 4px;
78
- }
79
- .ss-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text3); font-weight: 700; }
80
- .ss-val { font-family: var(--font-head); font-size: 20px; font-weight: 800; }
81
-
82
- .period-tabs {
83
- display: flex;
84
- gap: 4px;
85
- background: var(--bg3);
86
- border-radius: 8px;
87
- padding: 3px;
88
- }
89
- .period-tab {
90
- padding: 6px 14px;
91
- border-radius: 6px;
92
- border: none;
93
- background: transparent;
94
- color: var(--text2);
95
- font-size: 12px;
96
- font-weight: 700;
97
- cursor: pointer;
98
- font-family: var(--font-body);
99
- transition: all var(--transition);
100
- }
101
- .period-tab.active { background: var(--card); color: var(--cyan); }
102
-
103
- .sort-header { cursor: pointer; user-select: none; white-space: nowrap; }
104
- .sort-header:hover { color: var(--cyan); }
105
- .sort-arrow { margin-left: 4px; opacity: 0.5; font-size: 10px; }
106
-
107
- .delete-btn {
108
- background: none;
109
- border: none;
110
- color: var(--text3);
111
- cursor: pointer;
112
- font-size: 16px;
113
- padding: 4px;
114
- border-radius: 4px;
115
- transition: all var(--transition);
116
- }
117
- .delete-btn:hover { color: var(--rose); background: rgba(244,63,94,0.1); }
118
-
119
- .watch-tag {
120
- display: inline-flex;
121
- align-items: center;
122
- gap: 4px;
123
- font-size: 10px;
124
- font-weight: 700;
125
- padding: 2px 8px;
126
- border-radius: 100px;
127
- text-transform: uppercase;
128
- letter-spacing: 0.05em;
129
- }
130
- </style>
131
- </head>
132
- <body>
133
- <div class="app-shell">
134
- <nav class="sidebar">
135
- <div class="sidebar-logo">
136
- <div class="logo-mark">
137
- <div class="logo-icon">📈</div>
138
- <div><div class="logo-text">FinWise</div><div class="logo-sub">Smart Investing</div></div>
139
- </div>
140
- </div>
141
- <div class="nav-section">
142
- <div class="nav-label">Main</div>
143
- <a href="index.html" class="nav-item"><span class="nav-icon">🏠</span> Dashboard</a>
144
- <a href="portfolio.html" class="nav-item"><span class="nav-icon">📊</span> Portfolio Builder</a>
145
- <a href="risk.html" class="nav-item"><span class="nav-icon">🎯</span> Risk Analyzer</a>
146
- <a href="tracker.html" class="nav-item"><span class="nav-icon">📈</span> Tracker</a>
147
- <div class="nav-label">Tools</div>
148
- <a href="calculators.html" class="nav-item"><span class="nav-icon">🧮</span> Calculators</a>
149
- <a href="insights.html" class="nav-item"><span class="nav-icon">💡</span> Insights</a>
150
- </div>
151
- <div class="sidebar-footer">
152
- <div class="market-ticker">Live Market</div>
153
- <div id="sidebar-tickers"></div>
154
- </div>
155
- </nav>
156
-
157
- <main class="main-content">
158
- <div class="page-header fade-in">
159
- <div class="page-title">Investment <span>Tracker</span></div>
160
- <div class="page-subtitle">Track your holdings, gains, and portfolio performance</div>
161
- </div>
162
-
163
- <!-- Summary Strip -->
164
- <div class="summary-strip fade-in">
165
- <div class="ss-item">
166
- <div class="ss-label">Total Value</div>
167
- <div class="ss-val" id="total-val" style="color:var(--cyan)">—</div>
168
- </div>
169
- <div class="ss-item">
170
- <div class="ss-label">Total Cost</div>
171
- <div class="ss-val" id="total-cost">—</div>
172
- </div>
173
- <div class="ss-item">
174
- <div class="ss-label">Total Gain</div>
175
- <div class="ss-val" id="total-gain">—</div>
176
- </div>
177
- <div class="ss-item">
178
- <div class="ss-label">Gain %</div>
179
- <div class="ss-val" id="total-gain-pct">—</div>
180
- </div>
181
- <div class="ss-item">
182
- <div class="ss-label">Best Performer</div>
183
- <div class="ss-val" id="best-performer" style="color:var(--emerald)">—</div>
184
- </div>
185
- <div class="ss-item">
186
- <div class="ss-label">Holdings</div>
187
- <div class="ss-val" id="holdings-count" style="color:var(--violet)">—</div>
188
- </div>
189
- </div>
190
-
191
- <!-- Performance Chart -->
192
- <div class="card fade-in fade-in-1" style="margin-bottom:20px">
193
- <div class="flex justify-between items-center" style="margin-bottom:16px">
194
- <div class="card-title" style="margin-bottom:0">📈 Performance History</div>
195
- <div class="period-tabs">
196
- <button class="period-tab" onclick="setPeriod(7)">1W</button>
197
- <button class="period-tab" onclick="setPeriod(30)">1M</button>
198
- <button class="period-tab active" onclick="setPeriod(90)">3M</button>
199
- <button class="period-tab" onclick="setPeriod(180)">6M</button>
200
- <button class="period-tab" onclick="setPeriod(365)">1Y</button>
201
- </div>
202
- </div>
203
- <div style="height:220px;position:relative">
204
- <canvas id="perfChart"></canvas>
205
- </div>
206
- </div>
207
-
208
- <!-- Holdings Table -->
209
- <div class="card fade-in fade-in-2">
210
- <div class="tracker-header-bar">
211
- <div class="section-title" style="margin-bottom:0">📋 Holdings</div>
212
- <div class="flex gap-8">
213
- <input type="text" id="search-input" placeholder="🔍 Search ticker..." style="width:160px;padding:8px 12px;font-size:13px">
214
- <button class="btn btn-primary btn-sm" onclick="toggleAddForm()">+ Add Holding</button>
215
- </div>
216
- </div>
217
-
218
- <div class="table-wrap">
219
- <table id="holdings-table">
220
- <thead>
221
- <tr>
222
- <th>Asset</th>
223
- <th class="sort-header" onclick="sortBy('currentPrice')">Price <span class="sort-arrow">↕</span></th>
224
- <th class="sort-header" onclick="sortBy('shares')">Shares <span class="sort-arrow">↕</span></th>
225
- <th class="sort-header" onclick="sortBy('totalValue')">Value <span class="sort-arrow">↕</span></th>
226
- <th>Avg Cost</th>
227
- <th class="sort-header" onclick="sortBy('gain')">Gain/Loss <span class="sort-arrow">↕</span></th>
228
- <th class="sort-header" onclick="sortBy('gainPct')">Return <span class="sort-arrow">↕</span></th>
229
- <th>Alloc</th>
230
- <th></th>
231
- </tr>
232
- </thead>
233
- <tbody id="holdings-body"></tbody>
234
- </table>
235
- </div>
236
-
237
- <!-- Add Holding Form -->
238
- <div class="add-holding-form hidden" id="add-form">
239
- <div class="field-group" style="margin-bottom:0">
240
- <label class="field-label">Ticker</label>
241
- <select id="new-ticker" style="padding:9px 12px">
242
- <option value="">Select…</option>
243
- <option value="VOO">VOO — Vanguard S&P 500</option>
244
- <option value="QQQ">QQQ — Nasdaq 100</option>
245
- <option value="NVDA">NVDA — NVIDIA</option>
246
- <option value="AAPL">AAPL — Apple</option>
247
- <option value="AMZN">AMZN — Amazon</option>
248
- <option value="TSLA">TSLA — Tesla</option>
249
- <option value="BND">BND — Bond ETF</option>
250
- <option value="GLD">GLD — Gold Trust</option>
251
- <option value="WMT">WMT — Walmart</option>
252
- <option value="MCD">MCD — McDonald's</option>
253
- <option value="VTI">VTI — Total Market</option>
254
- </select>
255
- </div>
256
- <div class="field-group" style="margin-bottom:0">
257
- <label class="field-label">Shares</label>
258
- <input type="number" id="new-shares" placeholder="e.g. 5" min="0.001" step="0.001" style="padding:9px 12px">
259
- </div>
260
- <div class="field-group" style="margin-bottom:0">
261
- <label class="field-label">Avg Buy Price</label>
262
- <input type="number" id="new-cost" placeholder="e.g. 450.00" min="0" step="0.01" style="padding:9px 12px">
263
- </div>
264
- <div style="display:flex;gap:8px;align-items:flex-end">
265
- <button class="btn btn-emerald btn-full" onclick="addHolding()">Add</button>
266
- <button class="btn btn-ghost" onclick="toggleAddForm()">✕</button>
267
- </div>
268
- </div>
269
- </div>
270
-
271
- <!-- Allocation Chart + Distribution -->
272
- <div class="grid-2 fade-in fade-in-3" style="margin-top:20px">
273
- <div class="card">
274
- <div class="card-title">🥧 Portfolio Allocation</div>
275
- <div style="height:200px;position:relative">
276
- <canvas id="allocChart"></canvas>
277
- </div>
278
- </div>
279
- <div class="card">
280
- <div class="card-title">🏆 Top Performers</div>
281
- <div id="top-performers"></div>
282
- </div>
283
- </div>
284
-
285
- </main>
286
- </div>
287
-
288
- <nav class="bottom-nav">
289
- <div class="bottom-nav-inner">
290
- <a href="index.html" class="bottom-nav-item"><span class="bnav-icon">🏠</span>Home</a>
291
- <a href="portfolio.html" class="bottom-nav-item"><span class="bnav-icon">📊</span>Portfolio</a>
292
- <a href="risk.html" class="bottom-nav-item"><span class="bnav-icon">🎯</span>Risk</a>
293
- <a href="tracker.html" class="bottom-nav-item"><span class="bnav-icon">📈</span>Track</a>
294
- <a href="calculators.html" class="bottom-nav-item"><span class="bnav-icon">🧮</span>Calc</a>
295
- <a href="insights.html" class="bottom-nav-item"><span class="bnav-icon">💡</span>Insights</a>
296
- </div>
297
- </nav>
298
-
299
- <script src="shared.js"></script>
300
- <script>
301
- let holdings = [];
302
- let sortField = 'totalValue';
303
- let sortAsc = false;
304
- let perfChart = null, allocChart = null;
305
- let currentPeriod = 90;
306
-
307
- const AVG_COSTS = { VOO:420, QQQ:390, NVDA:640, AAPL:170, BND:74, GLD:190, AMZN:165, VTI:210, TSLA:210, WMT:63, MCD:270 };
308
-
309
- function initHoldings() {
310
- const portfolio = getPortfolio();
311
- holdings = portfolio.assets.map(a => ({
312
- ticker: a.ticker,
313
- name: a.name,
314
- color: a.color,
315
- type: a.type,
316
- shares: a.shares,
317
- currentPrice: MARKET_PRICES[a.ticker]?.price || a.price,
318
- avgCost: AVG_COSTS[a.ticker] || a.price * 0.9,
319
- }));
320
- computeAndRender();
321
- }
322
-
323
- function computeAndRender() {
324
- const totalVal = holdings.reduce((s,h) => s + h.shares * h.currentPrice, 0);
325
-
326
- holdings.forEach(h => {
327
- h.totalValue = h.shares * h.currentPrice;
328
- h.totalCost = h.shares * h.avgCost;
329
- h.gain = h.totalValue - h.totalCost;
330
- h.gainPct = h.totalCost > 0 ? (h.gain / h.totalCost) * 100 : 0;
331
- h.alloc = totalVal > 0 ? (h.totalValue / totalVal) * 100 : 0;
332
- });
333
-
334
- updateSummary(totalVal);
335
- renderTable();
336
- renderPerfChart(currentPeriod);
337
- renderAllocChart();
338
- renderTopPerformers();
339
- }
340
-
341
- function updateSummary(totalVal) {
342
- const totalCost = holdings.reduce((s,h) => s + h.totalCost, 0);
343
- const totalGain = totalVal - totalCost;
344
- const gainPct = totalCost > 0 ? (totalGain/totalCost)*100 : 0;
345
- const best = holdings.length > 0 ? holdings.reduce((m,h) => h.gainPct > m.gainPct ? h : m, holdings[0]) : null;
346
-
347
- document.getElementById('total-val').textContent = fmt$(totalVal);
348
- document.getElementById('total-cost').textContent = fmt$(totalCost);
349
- const gainEl = document.getElementById('total-gain');
350
- gainEl.textContent = (totalGain>=0?'+':'') + fmt$(totalGain);
351
- gainEl.style.color = totalGain >= 0 ? 'var(--emerald)' : 'var(--rose)';
352
- const gainPctEl = document.getElementById('total-gain-pct');
353
- gainPctEl.textContent = fmtPct(gainPct);
354
- gainPctEl.style.color = gainPct >= 0 ? 'var(--emerald)' : 'var(--rose)';
355
- document.getElementById('best-performer').textContent = best ? best.ticker : '—';
356
- document.getElementById('holdings-count').textContent = holdings.length;
357
- }
358
-
359
- function renderTable() {
360
- const query = document.getElementById('search-input').value.toLowerCase();
361
- let rows = [...holdings].filter(h => h.ticker.toLowerCase().includes(query) || h.name.toLowerCase().includes(query));
362
- rows.sort((a,b) => sortAsc ? a[sortField]-b[sortField] : b[sortField]-a[sortField]);
363
-
364
- const body = document.getElementById('holdings-body');
365
- body.innerHTML = rows.map((h,i) => {
366
- const mktData = MARKET_PRICES[h.ticker] || {};
367
- const dayChg = mktData.changePct || 0;
368
- const badgeClass = h.type==='ETF'?'badge-cyan':h.type==='Bond'?'badge-violet':h.type==='Commodity'?'badge-amber':'badge-emerald';
369
- return `
370
- <tr class="holding-row" id="row-${h.ticker}">
371
- <td>
372
- <div class="ticker-cell">
373
- <div class="ticker-logo" style="background:${h.color}">${h.ticker}</div>
374
- <div>
375
- <div style="font-weight:700">${h.ticker} <span class="badge ${badgeClass}" style="font-size:9px">${h.type}</span></div>
376
- <div class="ticker-name-sub">${h.name}</div>
377
- <div style="font-size:11px;margin-top:2px;color:${dayChg>=0?'var(--emerald)':'var(--rose)'}">${dayChg>=0?'▲':'▼'} ${Math.abs(dayChg).toFixed(2)}% today</div>
378
- </div>
379
- </div>
380
- </td>
381
- <td class="mono">${fmt$(h.currentPrice)}</td>
382
- <td class="mono">
383
- <input class="edit-input" type="number" value="${h.shares.toFixed(3)}" step="0.001" min="0"
384
- onchange="updateShares('${h.ticker}', this.value)">
385
- </td>
386
- <td class="mono bold">${fmt$(h.totalValue)}</td>
387
- <td class="mono">
388
- <input class="edit-input" type="number" value="${h.avgCost.toFixed(2)}" step="0.01" min="0"
389
- onchange="updateAvgCost('${h.ticker}', this.value)">
390
- </td>
391
- <td class="gain-cell ${h.gain>=0?'up':'down'}">${h.gain>=0?'+':''}${fmt$(h.gain)}</td>
392
- <td class="gain-cell ${h.gainPct>=0?'up':'down'}">${fmtPct(h.gainPct)}</td>
393
- <td style="min-width:90px">
394
- <div style="font-size:12px;font-family:var(--font-mono)">${h.alloc.toFixed(1)}%</div>
395
- <div class="pnl-bar-mini" style="width:${Math.min(h.alloc,100)}%;background:${h.color}"></div>
396
- </td>
397
- <td><button class="delete-btn" onclick="removeHolding('${h.ticker}')">🗑</button></td>
398
- </tr>
399
- `;
400
- }).join('');
401
- }
402
-
403
- function updateShares(ticker, val) {
404
- const h = holdings.find(h=>h.ticker===ticker);
405
- if (h) { h.shares = parseFloat(val)||0; computeAndRender(); }
406
- }
407
- function updateAvgCost(ticker, val) {
408
- const h = holdings.find(h=>h.ticker===ticker);
409
- if (h) { h.avgCost = parseFloat(val)||0; computeAndRender(); }
410
- }
411
- function removeHolding(ticker) {
412
- holdings = holdings.filter(h=>h.ticker!==ticker);
413
- computeAndRender();
414
- showToast(`${ticker} removed from tracker`);
415
- }
416
-
417
- function sortBy(field) {
418
- if (sortField === field) sortAsc = !sortAsc;
419
- else { sortField = field; sortAsc = false; }
420
- renderTable();
421
- }
422
-
423
- function setPeriod(days) {
424
- currentPeriod = days;
425
- document.querySelectorAll('.period-tab').forEach(b => b.classList.remove('active'));
426
- event.target.classList.add('active');
427
- renderPerfChart(days);
428
- }
429
-
430
- function renderPerfChart(days) {
431
- const totalCost = holdings.reduce((s,h)=>s+h.totalCost,0) || 10000;
432
- const history = generateHistory(days, totalCost);
433
- const ctx = document.getElementById('perfChart').getContext('2d');
434
- if (perfChart) perfChart.destroy();
435
-
436
- const labels = history.filter((_,i)=> days<=30 ? i%2===0 : days<=90 ? i%6===0 : i%14===0).map(d=>d.date);
437
- const values = history.filter((_,i)=> days<=30 ? i%2===0 : days<=90 ? i%6===0 : i%14===0).map(d=>d.value);
438
- const isUp = values[values.length-1] >= values[0];
439
-
440
- const grad = ctx.createLinearGradient(0,0,0,220);
441
- grad.addColorStop(0, isUp ? 'rgba(16,185,129,0.25)' : 'rgba(244,63,94,0.25)');
442
- grad.addColorStop(1, 'rgba(0,0,0,0)');
443
-
444
- perfChart = new Chart(ctx, {
445
- type:'line',
446
- data: { labels, datasets:[{
447
- data: values,
448
- borderColor: isUp ? '#10b981' : '#f43f5e',
449
- backgroundColor: grad,
450
- borderWidth: 2.5,
451
- fill: true,
452
- tension: 0.4,
453
- pointRadius: 0,
454
- pointHoverRadius: 5,
455
- }]},
456
- options: {
457
- responsive:true, maintainAspectRatio:false,
458
- plugins:{ legend:{display:false}, tooltip:{ callbacks:{ label: ctx => ' '+fmt$(ctx.raw) } } },
459
- scales:{
460
- x:{ grid:{display:false}, ticks:{font:{size:11}} },
461
- y:{ grid:{color:'rgba(34,211,238,0.06)'}, ticks:{ callback: v => '$'+(v/1000).toFixed(0)+'K', font:{size:11} } }
462
- },
463
- interaction:{ intersect:false, mode:'index' }
464
- }
465
- });
466
- }
467
-
468
- function renderAllocChart() {
469
- const ctx = document.getElementById('allocChart').getContext('2d');
470
- if (allocChart) allocChart.destroy();
471
- allocChart = new Chart(ctx, {
472
- type:'doughnut',
473
- data:{
474
- labels: holdings.map(h=>h.ticker),
475
- datasets:[{
476
- data: holdings.map(h=>h.alloc),
477
- backgroundColor: holdings.map(h=>h.color),
478
- borderColor:'rgba(5,13,26,0.8)',
479
- borderWidth:3,
480
- hoverOffset:6
481
- }]
482
- },
483
- options:{
484
- responsive:true, maintainAspectRatio:false,
485
- cutout:'68%',
486
- plugins:{ legend:{ position:'right', labels:{ color:'#8faac8', font:{size:11}, boxWidth:12 } } }
487
- }
488
- });
489
- }
490
-
491
- function renderTopPerformers() {
492
- const sorted = [...holdings].sort((a,b)=>b.gainPct-a.gainPct);
493
- document.getElementById('top-performers').innerHTML = sorted.slice(0,5).map((h,i) => `
494
- <div style="display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid rgba(34,211,238,0.06)">
495
- <div style="font-size:16px">${i===0?'🥇':i===1?'🥈':i===2?'🥉':'📊'}</div>
496
- <div class="ticker-logo" style="background:${h.color};width:30px;height:30px;font-size:8px">${h.ticker}</div>
497
- <div style="flex:1">
498
- <div style="font-weight:700;font-size:14px">${h.ticker}</div>
499
- <div style="font-size:11px;color:var(--text2)">${fmt$(h.totalValue)}</div>
500
- </div>
501
- <div style="font-family:var(--font-mono);font-weight:700;color:${h.gainPct>=0?'var(--emerald)':'var(--rose)'}">
502
- ${fmtPct(h.gainPct)}
503
- </div>
504
- </div>
505
- `).join('');
506
- }
507
-
508
- function toggleAddForm() {
509
- document.getElementById('add-form').classList.toggle('hidden');
510
- }
511
-
512
- function addHolding() {
513
- const ticker = document.getElementById('new-ticker').value;
514
- const shares = parseFloat(document.getElementById('new-shares').value);
515
- const cost = parseFloat(document.getElementById('new-cost').value);
516
- if (!ticker || !shares || !cost) { showToast('Please fill all fields', 'error'); return; }
517
- if (holdings.find(h=>h.ticker===ticker)) { showToast('Already in portfolio', 'error'); return; }
518
-
519
- const mkt = MARKET_PRICES[ticker] || { price: cost };
520
- const assetDef = { VOO:'Vanguard S&P 500 ETF', QQQ:'Invesco Nasdaq 100', NVDA:'NVIDIA Corp', AAPL:'Apple Inc.',
521
- AMZN:'Amazon.com Inc.', TSLA:'Tesla Inc.', BND:'Vanguard Bond ETF', GLD:'SPDR Gold Trust',
522
- WMT:'Walmart Inc.', MCD:"McDonald's Corp", VTI:'Vanguard Total Market' };
523
- const typeMap = { VOO:'ETF', VTI:'ETF', QQQ:'ETF', BND:'Bond', GLD:'Commodity', SLV:'Commodity' };
524
-
525
- holdings.push({
526
- ticker,
527
- name: assetDef[ticker] || ticker,
528
- color: ASSET_COLORS[holdings.length % ASSET_COLORS.length],
529
- type: typeMap[ticker] || 'Stock',
530
- shares,
531
- currentPrice: mkt.price,
532
- avgCost: cost,
533
- });
534
-
535
- document.getElementById('new-ticker').value = '';
536
- document.getElementById('new-shares').value = '';
537
- document.getElementById('new-cost').value = '';
538
- toggleAddForm();
539
- computeAndRender();
540
- showToast(`✅ ${ticker} added to tracker`);
541
- }
542
-
543
- document.getElementById('search-input').addEventListener('input', renderTable);
544
-
545
- document.addEventListener('DOMContentLoaded', () => {
546
- applyChartDefaults();
547
- initHoldings();
548
- });
549
- </script>
550
- </body>
551
- </html>