show_dividend
Calculate the annual dividend income for each holding in your portfolio, showing per-ticker yield, annual dividend per share, and estimated income from dividend-paying stocks.
Instructions
Estimated annual dividend income for all holdings. Returns per-ticker yield, annual DPS, and estimated income. Only includes tickers with dividend data.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- apps/mcp/src/tools/stock.ts:69-107 (registration)Registration of the 'show_dividend' tool on the MCP server. It calls server.tool() with the name 'show_dividend', a description, empty schema (no params), and an async handler.
server.tool( 'show_dividend', 'Estimated annual dividend income for all holdings. Returns per-ticker yield, annual DPS, and estimated income. Only includes tickers with dividend data.', {}, async () => { const db = getDb(); const txns = db.select().from(transactions).all(); const holdings = aggregateHoldings(txns); const priceMap = new Map( db .select() .from(prices) .all() .map((p) => [p.ticker, p]), ); const rows = [...holdings.entries()] .map(([ticker, h]) => { const p = priceMap.get(ticker); const dps = p?.dividend_per_share ?? null; const yieldPct = p?.dividend_yield ?? null; return { ticker, shares: h.shares, dividend_yield: yieldPct, dividend_per_share: dps, estimated_annual_income: dps != null ? dps * h.shares : null, }; }) .filter((r) => r.dividend_per_share != null); const total_annual = rows.reduce((s, r) => s + (r.estimated_annual_income ?? 0), 0); return ok({ holdings: rows, total_annual_income: total_annual, total_monthly_income: total_annual / 12, }); }, ); - apps/mcp/src/tools/stock.ts:73-106 (handler)The handler function that executes 'show_dividend' logic: queries DB for transactions, aggregates holdings, fetches prices, computes dividend yield/DPS/annual income per ticker, and returns holdings with total annual/monthly income.
async () => { const db = getDb(); const txns = db.select().from(transactions).all(); const holdings = aggregateHoldings(txns); const priceMap = new Map( db .select() .from(prices) .all() .map((p) => [p.ticker, p]), ); const rows = [...holdings.entries()] .map(([ticker, h]) => { const p = priceMap.get(ticker); const dps = p?.dividend_per_share ?? null; const yieldPct = p?.dividend_yield ?? null; return { ticker, shares: h.shares, dividend_yield: yieldPct, dividend_per_share: dps, estimated_annual_income: dps != null ? dps * h.shares : null, }; }) .filter((r) => r.dividend_per_share != null); const total_annual = rows.reduce((s, r) => s + (r.estimated_annual_income ?? 0), 0); return ok({ holdings: rows, total_annual_income: total_annual, total_monthly_income: total_annual / 12, }); }, - apps/mcp/src/tools/stock.ts:72-72 (schema)Empty schema object '{}' — the show_dividend tool takes no input parameters.
{}, - packages/db/src/aggregate.ts:3-30 (helper)The aggregateHoldings helper function that processes transactions into a map of ticker -> Holding (shares, costShares, totalCost), used by the show_dividend handler to determine current positions.
export const aggregateHoldings = (txns: Transaction[]): Map<string, Holding> => { const sorted = [...txns].sort((a, b) => a.date.localeCompare(b.date)); const map = sorted.reduce((acc, t) => { const h = acc.get(t.ticker) ?? { ticker: t.ticker, shares: 0, costShares: 0, totalCost: 0 }; if (t.type === 'buy') { h.shares += t.shares; h.costShares += t.shares; h.totalCost += t.shares * t.price; } else if (t.type === 'sell') { const ratio = h.shares > 0 ? (h.shares - t.shares) / h.shares : 0; h.shares -= t.shares; h.costShares = h.costShares * ratio; h.totalCost = h.totalCost * ratio; } else if (t.type === 'deposit') { h.shares += t.shares; if (t.price > 0) { h.costShares += t.shares; h.totalCost += t.shares * t.price; } } return acc.set(t.ticker, h); }, new Map<string, Holding>()); return new Map([...map.entries()].filter(([, h]) => h.shares > 0)); };