# My portfolio

```embedhtml
<style>
.mk{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;padding:2px}
.c{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:12px 14px}
.l{font:600 12px var(--body);color:var(--soft);text-transform:uppercase;letter-spacing:.05em}
.v{font:800 24px var(--display);margin:4px 0 2px;font-variant-numeric:tabular-nums}
.s{font-size:12px;color:var(--soft)}.up{color:var(--leaf)}.dn{color:var(--rose)}
.f{display:flex;justify-content:space-between;align-items:center;margin-top:6px;font-size:12px;color:var(--soft)}
.f button{border:0;background:none;color:var(--soft);cursor:pointer;font-size:12px;padding:0}
</style>
<div class="mk" id="mk"></div>
<div class="f"><span id="at">Market status, live from the sources</span><button id="go">↻ refresh</button></div>
<script>
// Live market status, fetched by the browser straight from the sources (no cache, no rules): metals from gold-api.com,
// USD/CAD from the ECB via frankfurter.app (open.er-api.com if that fails). Gold/silver is worked out here.
const T = [
  { k: "XAU", label: "Gold", unit: "US$/oz" }, { k: "XAG", label: "Silver", unit: "US$/oz" },
  { k: "HG", label: "Copper", unit: "US$/lb" }, { k: "GSR", label: "Gold/silver", unit: "ratio" },
  { k: "CAD", label: "USD/CAD", unit: "C$ per US$" }];
const V = {}, $ = id => document.getElementById(id);
const num = (x, d = 2) => x == null || isNaN(x) ? "—" : Number(x).toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });
function paint() {
  $("mk").innerHTML = T.map(t => { const v = V[t.k] || {}; const ch = v.prev ? (v.value - v.prev) / v.prev * 100 : null;
    return `<div class="c"><div class="l">${t.label}</div><div class="v">${v.err ? "—" : num(v.value, t.k === "CAD" ? 4 : 2)}</div>` +
      `<div class="s">${v.err ? "source unreachable" : t.unit}${ch != null ? ` · <span class="${ch >= 0 ? "up" : "dn"}">${ch >= 0 ? "+" : ""}${num(ch)}%</span>` : ""}</div></div>` }).join("");
}
const get = async u => { const r = await fetch(u); if (!r.ok) throw new Error(r.status); return r.json() };
async function metal(k) { try { const j = await get("https://api.gold-api.com/price/" + k); V[k] = { value: j.price } } catch (e) { V[k] = { err: 1 } } }
async function cad() {
  try { const d = new Date(Date.now() - 10 * 864e5).toISOString().slice(0, 10), j = await get(`https://api.frankfurter.app/${d}..?from=USD&to=CAD`);
    const r = Object.values(j.rates).map(x => x.CAD); V.CAD = { value: r[r.length - 1], prev: r[r.length - 2] } }
  catch (e) { try { const j = await get("https://open.er-api.com/v6/latest/USD"); V.CAD = { value: j.rates.CAD } } catch (e2) { V.CAD = { err: 1 } } }
}
async function load() {
  $("at").textContent = "Loading…";
  await Promise.all([metal("XAU"), metal("XAG"), metal("HG"), cad()]);
  V.GSR = V.XAU.err || V.XAG.err ? { err: 1 } : { value: V.XAU.value / V.XAG.value };
  paint(); $("at").textContent = "Live, " + new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
}
$("go").onclick = load; paint(); load(); setInterval(load, 120000);
</script>
```

```table
class: Holding
columns: company, account, shares, avgCost as Avg cost, company.price as Price, value, pl as P/L, plPct as P/L %
totals: cost, value, pl
sort: value desc
color: pl, plPct
```

## News on what you hold

```table
class: News
columns: date, company, headline, kind
sort: date desc
```

This is the **portfolio example**: what an AI builds when you tell it

> Build me an app to track my portfolio: my holdings, what I paid, today's price, and tell me when a company I hold puts out news.

Everything here is sample data. Tap a holding to change it, or **+ Add** a new one; value and P/L work themselves out. The forms all work, but this demo doesn't save: that's for your own project. The model is in [[Spec:portfolio]] and the samples in [[Story:portfolio-samples]]; [Data](/dom) has everything, companies included.

## Make it yours

- **In your own project:** connect your AI (see [Help](/help)) and ask it to *import the portfolio example*. It copies the model and the samples, and adds a card on your home page.
- **Change it by talking:** "add a watchlist", "track dividends", "tell me when a price moves more than 5%". Your AI changes the model and the app follows.

This demo is reset whenever d2 is updated: nothing you change here is kept.
