Skip to content
LAM
Read Home Blog
Make Projects HTML Tools Games
Touch grass Notes Resume Links
Home Blog HTML Projects
Tools Games Notes Resume Links
Back penguin's real road to $1m Finance
Download Open
Show description 604 chars · Finance

penguin's real road to $1m

penguin's real road to $1m







🐧


penguin's realistic road to $1,000,000

12-18 month journey | 60% win rate | discipline over luck | usdc parking between setups







⚠️ this is a hypothetical projection based on realistic market scenarios, proper risk management, and disciplined execution.
most traders (99.9%) never make it. requires perfect discipline, emotional control, and market timing.





starting bankroll
$100




target
$1,000,000




timeline
14 months




total trades
47 swings




win rate
59.6%




avg winner
-3.2%




max drawdown

penguin's real road to $1m

23,291 bytes · HTML source
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>penguin's real road to $1m</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <style>
    :root {
      --bg: #0a0e14;
      --bg-card: #0f1419;
      --accent: #00d9ff;
      --accent-dim: #00d9ff33;
      --text: #e6edf3;
      --text-dim: #8b949e;
      --green: #3fb950;
      --red: #f85149;
      --yellow: #d29922;
      --border: #30363d;
    }
    
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    body {
      background: var(--bg);
      color: var(--text);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      padding: 20px;
      line-height: 1.6;
    }
    
    .container {
      max-width: 1400px;
      margin: 0 auto;
    }
    
    header {
      display: flex;
      align-items: center;
      gap: 20px;
      margin-bottom: 30px;
      padding: 20px;
      background: var(--bg-card);
      border: 1px solid var(--border);
      border-radius: 8px;
    }
    
    .penguin {
      font-size: 48px;
    }
    
    .header-content h1 {
      font-size: 24px;
      margin-bottom: 5px;
    }
    
    .header-content p {
      color: var(--text-dim);
      font-size: 14px;
    }
    
    .stats {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 15px;
      margin-bottom: 20px;
    }
    
    .stat-card {
      background: var(--bg-card);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 15px;
    }
    
    .stat-card label {
      color: var(--text-dim);
      font-size: 12px;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    
    .stat-card .value {
      font-size: 24px;
      font-weight: 600;
      margin-top: 5px;
    }
    
    .grid-2 {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 20px;
      margin-bottom: 20px;
    }
    
    @media (max-width: 900px) {
      .grid-2 {
        grid-template-columns: 1fr;
      }
    }
    
    .card {
      background: var(--bg-card);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 20px;
    }
    
    .card h2 {
      font-size: 16px;
      margin-bottom: 15px;
      color: var(--accent);
      text-transform: uppercase;
      letter-spacing: 1px;
    }
    
    .card p {
      color: var(--text-dim);
      font-size: 13px;
      margin-bottom: 15px;
    }
    
    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13px;
    }
    
    th, td {
      padding: 8px;
      text-align: left;
      border-bottom: 1px solid var(--border);
    }
    
    th {
      color: var(--text-dim);
      font-weight: 600;
      text-transform: uppercase;
      font-size: 11px;
      letter-spacing: 0.5px;
    }
    
    tr:hover {
      background: rgba(255,255,255,0.03);
    }
    
    .green { color: var(--green); }
    .red { color: var(--red); }
    .yellow { color: var(--yellow); }
    
    .badge {
      display: inline-block;
      padding: 2px 8px;
      border-radius: 4px;
      font-size: 11px;
      font-weight: 600;
    }
    
    .badge-win {
      background: var(--green);
      color: #000;
    }
    
    .badge-loss {
      background: var(--red);
      color: #fff;
    }
    
    .badge-park {
      background: var(--text-dim);
      color: #000;
    }
    
    canvas {
      max-height: 300px;
    }
    
    .warning {
      background: rgba(210, 153, 34, 0.1);
      border: 1px solid var(--yellow);
      border-radius: 4px;
      padding: 10px;
      margin-bottom: 15px;
      font-size: 12px;
    }
    
    .rules {
      background: rgba(0, 217, 255, 0.05);
      border: 1px solid var(--accent-dim);
      border-radius: 4px;
      padding: 15px;
      margin-top: 15px;
    }
    
    .rules h3 {
      font-size: 14px;
      margin-bottom: 10px;
      color: var(--accent);
    }
    
    .rules ul {
      margin-left: 20px;
      font-size: 13px;
    }
    
    .rules li {
      margin-bottom: 5px;
      color: var(--text-dim);
    }
    
    .phase {
      background: rgba(63, 185, 80, 0.1);
      border-left: 3px solid var(--green);
      padding: 5px 10px;
      margin: 5px 0;
      font-size: 12px;
      font-weight: 600;
    }
  </style>
</head>
<body>
<div class="container">
  <header>
    <div class="penguin">🐧</div>
    <div class="header-content">
      <h1>penguin's realistic road to $1,000,000</h1>
      <p>12-18 month journey | 60% win rate | discipline over luck | usdc parking between setups</p>
    </div>
  </header>

  <div class="warning">
    ⚠️ this is a hypothetical projection based on realistic market scenarios, proper risk management, and disciplined execution. 
    most traders (99.9%) never make it. requires perfect discipline, emotional control, and market timing.
  </div>

  <div class="stats">
    <div class="stat-card">
      <label>starting bankroll</label>
      <div class="value">$100</div>
    </div>
    <div class="stat-card">
      <label>target</label>
      <div class="value" style="color: var(--green)">$1,000,000</div>
    </div>
    <div class="stat-card">
      <label>timeline</label>
      <div class="value">14 months</div>
    </div>
    <div class="stat-card">
      <label>total trades</label>
      <div class="value">47 swings</div>
    </div>
    <div class="stat-card">
      <label>win rate</label>
      <div class="value">59.6%</div>
    </div>
    <div class="stat-card">
      <label>avg winner</label>
      <div class="value class="green">+28.4%</div>
    </div>
    <div class="stat-card">
      <label>avg loser</label>
      <div class="value class="red">-3.2%</div>
    </div>
    <div class="stat-card">
      <label>max drawdown</label>
      <div class="value class="red">-22.8%</div>
    </div>
  </div>

  <div class="grid-2">
    <div class="card">
      <h2>live market prices</h2>
      <p>real-time data from coingecko free api | updates every 30s</p>
      <table id="priceTable">
        <thead>
          <tr>
            <th>coin</th>
            <th>price</th>
            <th>24h change</th>
            <th>volume</th>
          </tr>
        </thead>
        <tbody>
          <tr><td colspan="4" style="text-align:center; color: var(--text-dim)">loading prices...</td></tr>
        </tbody>
      </table>
    </div>

    <div class="card">
      <h2>penguin's core rules</h2>
      <div class="rules">
        <h3>never break these:</h3>
        <ul>
          <li>4+ confirmed signals required (rsi, volume, btc context, trend)</li>
          <li>position sizing: 60-80% on best setups, NEVER 100%</li>
          <li>hard stop at -3% per position, -8% daily limit</li>
          <li>take profits at +15%, +25%, trail the rest</li>
          <li>after 2 losses: turtle mode (60% size, 5 signals required)</li>
          <li>park in usdc between setups - patience > fomo</li>
          <li>no late night revenge trading (sleep management critical)</li>
        </ul>
      </div>
    </div>
  </div>

  <div class="card" style="margin-bottom: 20px;">
    <h2>equity curve - the realistic grind</h2>
    <p>note the plateaus (usdc parking), drawdowns (stopped out), and steady compound from discipline</p>
    <canvas id="equityChart"></canvas>
  </div>

  <div class="card">
    <h2>complete trade log - $100 to $1,001,342</h2>
    <p>47 trades over 14 months | includes winners, losers, and usdc parking periods</p>
    <div style="overflow-x: auto;">
      <table id="tradeTable">
        <thead>
          <tr>
            <th>#</th>
            <th>phase</th>
            <th>month</th>
            <th>coin</th>
            <th>type</th>
            <th>return</th>
            <th>equity before</th>
            <th>equity after</th>
            <th>reasoning</th>
          </tr>
        </thead>
        <tbody>
          <!-- filled by js -->
        </tbody>
      </table>
    </div>
  </div>
</div>

<script>
// coins to track with coingecko
const COINS = [
  { symbol: 'BTC', id: 'bitcoin', name: 'Bitcoin' },
  { symbol: 'ETH', id: 'ethereum', name: 'Ethereum' },
  { symbol: 'SOL', id: 'solana', name: 'Solana' },
  { symbol: 'RNDR', id: 'render-token', name: 'Render' },
  { symbol: 'FET', id: 'fetch-ai', name: 'Fetch.ai' },
  { symbol: 'TAO', id: 'bittensor', name: 'Bittensor' },
  { symbol: 'ONDO', id: 'ondo-finance', name: 'Ondo' },
  { symbol: 'GRT', id: 'the-graph', name: 'The Graph' },
  { symbol: 'LINK', id: 'chainlink', name: 'Chainlink' },
  { symbol: 'SUI', id: 'sui', name: 'Sui' },
  { symbol: 'SEI', id: 'sei-network', name: 'Sei' },
  { symbol: 'AR', id: 'arweave', name: 'Arweave' }
];

// realistic trade journey - 47 trades over 14 months
const trades = [
  // PHASE 1: SURVIVAL ($100 → $500) - months 1-3
  {trade:1, phase:'1: survival', month:1, coin:'BTC', type:'win', returnPct:18.2, equityBefore:100, equityAfter:118.20, reasoning:'btc -4% flash crash, rsi 28, strong bounce at $26.8k support'},
  {trade:2, phase:'1: survival', month:1, coin:'ETH', type:'win', returnPct:22.4, equityBefore:118.20, equityAfter:144.67, reasoning:'btc stable, eth showing relative strength, breakout from $1,650 resistance'},
  {trade:3, phase:'1: survival', month:1, coin:'USDC', type:'park', returnPct:0, equityBefore:144.67, equityAfter:144.67, reasoning:'waiting for next 4+ signal setup, avoiding chop'},
  {trade:4, phase:'1: survival', month:2, coin:'SOL', type:'loss', returnPct:-3.0, equityBefore:144.67, equityAfter:140.33, reasoning:'false breakout at $22, stopped out as volume failed'},
  {trade:5, phase:'1: survival', month:2, coin:'BTC', type:'win', returnPct:15.8, equityBefore:140.33, equityAfter:162.49, reasoning:'turtle mode after loss, waited for weekly rsi 45, solid entry at $28.2k'},
  {trade:6, phase:'1: survival', month:2, coin:'USDC', type:'park', returnPct:0, equityBefore:162.49, equityAfter:162.49, reasoning:'market choppy, preserving capital'},
  {trade:7, phase:'1: survival', month:3, coin:'ETH', type:'win', returnPct:28.3, equityBefore:162.49, equityAfter:208.45, reasoning:'eth etf hype building, volume 3x, broke $1,750 clean'},
  {trade:8, phase:'1: survival', month:3, coin:'SOL', type:'win', returnPct:31.7, equityBefore:208.45, equityAfter:274.52, reasoning:'solana phone launch momentum, firedancer hype, $24→$31'},
  {trade:9, phase:'1: survival', month:3, coin:'USDC', type:'park', returnPct:0, equityBefore:274.52, equityAfter:274.52, reasoning:'hit $500 target early, consolidating before phase 2'},
  
  // PHASE 2: BUILDING BASE ($500 → $2,500) - months 4-6
  {trade:10, phase:'2: base building', month:4, coin:'BTC', type:'win', returnPct:24.1, equityBefore:274.52, equityAfter:340.68, reasoning:'btc dominance dropping, perfect setup at $30k psychological level'},
  {trade:11, phase:'2: base building', month:4, coin:'AVAX', type:'loss', returnPct:-3.1, equityBefore:340.68, equityAfter:330.12, reasoning:'entered too early on subnet news, market wasnt ready'},
  {trade:12, phase:'2: base building', month:4, coin:'USDC', type:'park', returnPct:0, equityBefore:330.12, equityAfter:330.12, reasoning:'turtle mode, need 5 signals next'},
  {trade:13, phase:'2: base building', month:5, coin:'SOL', type:'win', returnPct:42.3, equityBefore:330.12, equityAfter:469.75, reasoning:'solana defi summer, jup airdrop hype, $28→$40'},
  {trade:14, phase:'2: base building', month:5, coin:'ETH', type:'win', returnPct:19.2, equityBefore:469.75, equityAfter:559.94, reasoning:'eth etf approval imminent rumors, $1,850→$2,200'},
  {trade:15, phase:'2: base building', month:5, coin:'USDC', type:'park', returnPct:0, equityBefore:559.94, equityAfter:559.94, reasoning:'waiting for alt rotation signals'},
  {trade:16, phase:'2: base building', month:6, coin:'RNDR', type:'win', returnPct:38.6, equityBefore:559.94, equityAfter:776.06, reasoning:'ai narrative heating up, apple vision pro launch, $2.80→$3.90'},
  {trade:17, phase:'2: base building', month:6, coin:'FET', type:'win', returnPct:45.2, equityBefore:776.06, equityAfter:1127.02, reasoning:'ai agent narrative exploding, $0.42→$0.61'},
  {trade:18, phase:'2: base building', month:6, coin:'BTC', type:'win', returnPct:21.8, equityBefore:1127.02, equityAfter:1372.73, reasoning:'btc testing $35k, halving 6 months out, accumulation clear'},
  {trade:19, phase:'2: base building', month:6, coin:'USDC', type:'park', returnPct:0, equityBefore:1372.73, equityAfter:1372.73, reasoning:'profit taking, waiting for next phase setup'},
  {trade:20, phase:'2: base building', month:7, coin:'SOL', type:'win', returnPct:35.4, equityBefore:1372.73, equityAfter:1858.66, reasoning:'solana breakout from $45 consolidation, defi tvl surging'},
  {trade:21, phase:'2: base building', month:7, coin:'ETH', type:'win', returnPct:18.9, equityBefore:1858.66, equityAfter:2210.01, reasoning:'eth following btc strength, $2,400→$2,850'},
  {trade:22, phase:'2: base building', month:7, coin:'USDC', type:'park', returnPct:0, equityBefore:2210.01, equityAfter:2210.01, reasoning:'phase 2 complete, preparing for sector rotation'},

  // PHASE 3: SECTOR ROTATION ($2,500 → $10,000) - months 8-11
  {trade:23, phase:'3: sector rotation', month:8, coin:'ONDO', type:'win', returnPct:52.3, equityBefore:2210.01, equityAfter:3365.55, reasoning:'rwa narrative exploding, blackrock tokenization news, $0.85→$1.30'},
  {trade:24, phase:'3: sector rotation', month:8, coin:'GRT', type:'win', returnPct:28.7, equityBefore:3365.55, equityAfter:4331.46, reasoning:'indexing critical for ai data, new protocol integrations, $0.14→$0.18'},
  {trade:25, phase:'3: sector rotation', month:8, coin:'USDC', type:'park', returnPct:0, equityBefore:4331.46, equityAfter:4331.46, reasoning:'market overheated short term, patience'},
  {trade:26, phase:'3: sector rotation', month:9, coin:'TAO', type:'win', returnPct:67.8, equityBefore:4331.46, equityAfter:7268.05, reasoning:'bittensor subnet craze, ai narrative peak, $240→$402'},
  {trade:27, phase:'3: sector rotation', month:9, coin:'LINK', type:'loss', returnPct:-2.9, equityBefore:7268.05, equityAfter:7057.23, reasoning:'ccip news priced in, took quick stop'},
  {trade:28, phase:'3: sector rotation', month:9, coin:'USDC', type:'park', returnPct:0, equityBefore:7057.23, equityAfter:7057.23, reasoning:'after loss, strict turtle rules apply'},
  {trade:29, phase:'3: sector rotation', month:10, coin:'BTC', type:'win', returnPct:32.4, equityBefore:7057.23, equityAfter:9344.17, reasoning:'btc etf approval odds rising, $38k→$50k breakout'},
  {trade:30, phase:'3: sector rotation', month:10, coin:'USDC', type:'park', returnPct:0, equityBefore:9344.17, equityAfter:9344.17, reasoning:'hit $10k milestone, consolidating before next phase'},

  // PHASE 4: HIGH CONVICTION ($10,000 → $35,000) - months 11-13
  {trade:31, phase:'4: high conviction', month:11, coin:'SOL', type:'win', returnPct:48.2, equityBefore:9344.17, equityAfter:13849.06, reasoning:'solana at $75, eth killer narrative resurfacing, jito staking boom'},
  {trade:32, phase:'4: high conviction', month:11, coin:'ETH', type:'win', returnPct:41.5, equityBefore:13849.06, equityAfter:19595.88, reasoning:'eth etf inflows, $3,200→$4,500 in 3 weeks'},
  {trade:33, phase:'4: high conviction', month:11, coin:'USDC', type:'park', returnPct:0, equityBefore:19595.88, equityAfter:19595.88, reasoning:'thanksgiving, low volume period'},
  {trade:34, phase:'4: high conviction', month:12, coin:'RNDR', type:'win', returnPct:55.3, equityBefore:19595.88, equityAfter:30433.18, reasoning:'render for apple vision pro ecosystem, $6.20→$9.60'},
  {trade:35, phase:'4: high conviction', month:12, coin:'LINK', type:'loss', returnPct:-3.2, equityBefore:30433.18, equityAfter:29459.42, reasoning:'swift partnership news leak caused early pump then dump'},
  {trade:36, phase:'4: high conviction', month:12, coin:'USDC', type:'park', returnPct:0, equityBefore:29459.42, equityAfter:29459.42, reasoning:'holiday consolidation, respecting turtle mode'},
  {trade:37, phase:'4: high conviction', month:13, coin:'BTC', type:'win', returnPct:28.9, equityBefore:29459.42, equityAfter:37973.50, reasoning:'btc etf launch momentum, $52k→$67k'},

  // PHASE 5: CALCULATED ASYMMETRY ($35,000 → $100,000) - months 13-15
  {trade:38, phase:'5: asymmetry', month:13, coin:'SUI', type:'win', returnPct:73.4, equityBefore:37973.50, equityAfter:65844.53, reasoning:'sui gaming narrative, defi yields, hidden gem at $1.40→$2.40'},
  {trade:39, phase:'5: asymmetry', month:14, coin:'TAO', type:'loss', returnPct:-3.0, equityBefore:65844.53, equityAfter:63869.19, reasoning:'entered at $680, got stopped at support break'},
  {trade:40, phase:'5: asymmetry', month:14, coin:'USDC', type:'park', returnPct:0, equityBefore:63869.19, equityAfter:63869.19, reasoning:'drawdown management, waiting for clear setup'},
  {trade:41, phase:'5: asymmetry', month:14, coin:'SEI', type:'win', returnPct:62.8, equityBefore:63869.19, equityAfter:103978.01, reasoning:'fastest l1 narrative, cosmos ibc boost, $0.52→$0.85'},

  // PHASE 6: THE BIG SWING ($100,000 → $300,000) - months 15-17
  {trade:42, phase:'6: big swing', month:15, coin:'ETH', type:'win', returnPct:89.4, equityBefore:103978.01, equityAfter:196940.61, reasoning:'THIS IS IT - eth breaking out vs btc, $4,800→$9,100, etf inflows massive'},
  {trade:43, phase:'6: big swing', month:16, coin:'SOL', type:'win', returnPct:42.3, equityBefore:196940.61, equityAfter:280246.93, reasoning:'solana etf approval, $180→$256'},
  {trade:44, phase:'6: big swing', month:16, coin:'USDC', type:'park', returnPct:0, equityBefore:280246.93, equityAfter:280246.93, reasoning:'hit phase 6 target, being patient for final push'},

  // PHASE 7: PRESERVATION ($300,000 → $1,000,000) - months 17-14
  {trade:45, phase:'7: preservation', month:17, coin:'BTC', type:'win', returnPct:52.8, equityBefore:280246.93, equityAfter:428297.15, reasoning:'btc final euphoria stage, $75k→$115k, institutions piling in'},
  {trade:46, phase:'7: preservation', month:18, coin:'ETH', type:'win', returnPct:38.2, equityBefore:428297.15, equityAfter:591982.23, reasoning:'eth following btc, $9,500→$13,100'},
  {trade:47, phase:'7: preservation', month:18, coin:'SOL', type:'win', returnPct:69.1, equityBefore:591982.23, equityAfter:1001342.30, reasoning:'solana final moonshot, flippening eth narrative, $280→$474'}
];

// render trade table
function renderTradeTable() {
  const tbody = document.querySelector('#tradeTable tbody');
  tbody.innerHTML = '';
  
  trades.forEach(t => {
    const tr = document.createElement('tr');
    const typeClass = t.type === 'win' ? 'green' : t.type === 'loss' ? 'red' : 'text-dim';
    const badge = t.type === 'win' ? 'badge-win' : t.type === 'loss' ? 'badge-loss' : 'badge-park';
    
    tr.innerHTML = `
      <td>${t.trade}</td>
      <td><span class="phase">${t.phase}</span></td>
      <td>month ${t.month}</td>
      <td><strong>${t.coin}</strong></td>
      <td><span class="badge ${badge}">${t.type}</span></td>
      <td class="${typeClass}"><strong>${t.returnPct > 0 ? '+' : ''}${t.returnPct.toFixed(1)}%</strong></td>
      <td>$${t.equityBefore.toLocaleString(undefined, {maximumFractionDigits:2})}</td>
      <td>$${t.equityAfter.toLocaleString(undefined, {maximumFractionDigits:2})}</td>
      <td style="color: var(--text-dim); font-size: 12px;">${t.reasoning}</td>
    `;
    tbody.appendChild(tr);
  });
}

// equity curve chart
function buildEquityChart() {
  const ctx = document.getElementById('equityChart').getContext('2d');
  const labels = trades.map(t => `#${t.trade}`);
  const data = trades.map(t => t.equityAfter);
  
  new Chart(ctx, {
    type: 'line',
    data: {
      labels,
      datasets: [{
        label: 'Account Equity',
        data,
        borderColor: '#00d9ff',
        backgroundColor: 'rgba(0, 217, 255, 0.1)',
        borderWidth: 2,
        fill: true,
        tension: 0.1,
        pointRadius: 2,
        pointHoverRadius: 4
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: { display: false },
        tooltip: {
          backgroundColor: '#0f1419',
          titleColor: '#e6edf3',
          bodyColor: '#8b949e',
          borderColor: '#30363d',
          borderWidth: 1,
          callbacks: {
            label: (context) => {
              const trade = trades[context.dataIndex];
              return [
                `equity: $${context.parsed.y.toLocaleString(undefined, {maximumFractionDigits:2})}`,
                `coin: ${trade.coin}`,
                `return: ${trade.returnPct > 0 ? '+' : ''}${trade.returnPct.toFixed(1)}%`
              ];
            }
          }
        }
      },
      scales: {
        x: {
          ticks: { color: '#8b949e' },
          grid: { color: '#30363d' }
        },
        y: {
          ticks: {
            color: '#8b949e',
            callback: (value) => '$' + value.toLocaleString()
          },
          grid: { color: '#30363d' }
        }
      }
    }
  });
}

// fetch live prices from coingecko
async function fetchPrices() {
  const tbody = document.querySelector('#priceTable tbody');
  try {
    const ids = COINS.map(c => c.id).join(',');
    const url = `https://api.coingecko.com/api/v3/simple/price?ids=${ids}&vs_currencies=usd&include_24hr_change=true&include_24hr_vol=true`;
    
    const res = await fetch(url);
    if (!res.ok) throw new Error('API error');
    const data = await res.json();
    
    tbody.innerHTML = '';
    
    COINS.forEach(coin => {
      const info = data[coin.id];
      if (!info) return;
      
      const tr = document.createElement('tr');
      const changeClass = info.usd_24h_change > 0 ? 'green' : info.usd_24h_change < 0 ? 'red' : '';
      
      tr.innerHTML = `
        <td><strong>${coin.symbol}</strong> <span style="color: var(--text-dim); font-size: 11px;">${coin.name}</span></td>
        <td>$${info.usd.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 6})}</td>
        <td class="${changeClass}"><strong>${info.usd_24h_change > 0 ? '+' : ''}${info.usd_24h_change.toFixed(2)}%</strong></td>
        <td style="color: var(--text-dim)">$${(info.usd_24h_vol / 1e6).toFixed(1)}M</td>
      `;
      tbody.appendChild(tr);
    });
  } catch (err) {
    tbody.innerHTML = `<tr><td colspan="4" style="text-align:center; color: var(--red)">failed to load prices: ${err.message}</td></tr>`;
  }
}

// init
renderTradeTable();
buildEquityChart();
fetchPrices();
setInterval(fetchPrices, 30000); // refresh every 30s
</script>
</body>
</html>