// Generated, do not edit. sha256:a35de9757194e9058e50a8d62faae8adcfd1b35f42f9b9b14cece1c6cab04e85 import { accountHoldings, positionsByAsset } from '../data/positions.js'; import { listAccounts } from '../data/accounts.js'; import { isOwnWealth } from '../../shared/wealth-scope.js'; import { latestPricePoint } from '../data/price-history.js'; import { assertScope } from '../ai/gate.js'; import { limitedList } from '../ai/limits.js'; import { shareBucket, shareLabel } from '../ai/buckets.js'; import { shareDisclosure } from '../ai/precision.js'; import { vertalerVoor } from './session.mjs'; import { assetMetrics } from '../../shared/wealth-metrics.js'; import { isMetal, metalFactor } from '../../shared/units.js'; import { getBaseCurrency } from '../data/portfolio.js'; function verdelingsblok(rijen, totaal, t) { if (!(totaal > 0)) return []; const vorm = shareDisclosure(t.precision); if (vorm === 'omit') { return [ 'De verdeling over de assets komt niet mee: met een exact totaal én exacte percentages ligt de ' + 'waarde per asset vast, en de bedragen per regel zijn juist wél in marges. Zet één van beide ' + 'terug op marges (of zet onbewerkt delen aan) om hem te zien.', '', ]; } const pctUit = pct => (vorm === 'exact' ? `${pct.toFixed(1)}%` : shareLabel(pct)); const posten = []; let overigPct = 0; let overigN = 0; let zonderKoers = 0; for (const { p, m } of rijen) { if (m.value == null) { zonderKoers += 1; continue; } const pct = (m.value / totaal) * 100; const b = shareBucket(pct); if (!b || b.below) { overigPct += pct; overigN += 1; continue; } posten.push({ symbool: t.toAi(p, { entity: 'asset' }).symbol, pct }); } if (!posten.length && !overigN) return []; posten.sort((a, b) => b.pct - a.pct); const regels = limitedList(posten, x => `- ${x.symbool}: ${pctUit(x.pct)}`).lines; if (overigN) { regels.push(`- overig (${overigN} ${overigN === 1 ? 'asset' : 'assets'}): ${pctUit(overigPct)}`); } return [ vorm === 'exact' ? 'Verdeling over de portefeuille (samen dekken ze het geheel):' : 'Verdeling over de portefeuille (marges; samen dekken ze het geheel):', ...regels, ...(zonderKoers ? [`(${zonderKoers} zonder koers, dus niet in deze verdeling.)`] : []), '', ]; } function buitenTotalen(decimalen) { const buiten = listAccounts().filter(a => !isOwnWealth(a)); if (!buiten.length) return []; const soort = new Map(buiten.map(a => [a.id, a.wealthScope])); const som = new Map([...new Set(buiten.map(a => a.wealthScope))].map(s => [s, { waarde: 0, zonderKoers: 0 }])); for (const r of accountHoldings(buiten.map(a => a.id))) { const punt = latestPricePoint(r.assetId); const post = som.get(soort.get(r.accountId)); if (!punt || !decimalen.has(r.assetId)) { post.zonderKoers += 1; continue; } post.waarde += (r.saldo / Math.pow(10, decimalen.get(r.assetId))) * punt.fiatPrice; } return ['managed', 'none'].filter(s => som.has(s)).map(s => ({ soort: s, ...som.get(s) })); } const BUITEN_LABEL = { managed: 'vermogen van derden', none: 'telt niet mee' }; const wealthSummary = Object.freeze({ name: 'wealth_summary', title: 'Vermogen in cijfers', description: 'Geeft wat de portefeuille van AssetApp nu waard is: het totaal, en per asset de hoeveelheid, ' + 'koers, waarde, inleg en het ongerealiseerde resultaat. Staat onbewerkt delen uit, dan komen ' + 'het totaal en de verdeling per asset terug als marges, zonder bedrag per asset. De koers is ' + 'het laatst opgeslagen punt en geen verse marktkoers. Wijzigt niets.', inputSchema: Object.freeze({ type: 'object', properties: {}, additionalProperties: false }), scope: 'read', async run(_args, context) { assertScope(wealthSummary.scope); const t = await vertalerVoor(context); t.beginCall(); const onbewerkt = t.precision.rawValues; const posities = positionsByAsset(undefined, { ownOnly: true }); const cur = getBaseCurrency(); const money = n => `${cur} ${n.toFixed(2)}`; const totaalTekst = n => { const uit = t.toAi({ fiatValue: n }, { entity: 'wealth', role: 'total' }).fiatValue; return typeof uit === 'number' ? money(uit) : `${cur} ${uit}`; }; const buiten = buitenTotalen(new Map(posities.map(p => [p.id, p.decimals]))); const buitenRegel = !buiten.length ? [] : [ `Niet in dit totaal (Vermogen op het account): ${buiten.map(b => `${BUITEN_LABEL[b.soort]} ${totaalTekst(b.waarde)}` + (b.zonderKoers ? ` (${b.zonderKoers} zonder koers)` : '')).join('; ')}.`, '', ]; const buitenVelden = buiten.length ? ['wealthScope'] : []; let totaal = 0; let zonderKoers = 0; const rijen = []; for (const p of posities) { if (p.net === 0 && p.buy_events === 0) continue; const punt = latestPricePoint(p.id); const m = assetMetrics(p, punt ? punt.fiatPrice : null); if (m.value != null) totaal += m.value; else zonderKoers += 1; rijen.push({ p, m, punt }); } if (!rijen.length) { if (buiten.length) { return { text: ['Deze portefeuille heeft geen eigen bezit.', '', ...buitenRegel].join('\n').trim(), fieldsShared: ['baseCurrency', 'fiatValue', ...buitenVelden] }; } return { text: 'Deze portefeuille heeft nog geen bezit: er is niets om een waarde van te geven.', fieldsShared: [] }; } const kop = `Totale waarde nu: ${totaalTekst(totaal)}` + ` over ${rijen.length} ${rijen.length === 1 ? 'asset' : 'assets'}` + (zonderKoers ? `, waarvan ${zonderKoers} zonder koers en dus zonder waarde` : '') + '.'; if (!onbewerkt) { return { text: [kop, '', ...buitenRegel, ...verdelingsblok(rijen, totaal, t), 'Per asset komen de BEDRAGEN niet mee zolang onbewerkt delen uit staat: een waarde per asset ' + 'met de koers erbij legt de hoeveelheid vast. Zet die schuif aan voor een portefeuille ' + 'zonder echte gegevens.', ].join('\n'), fieldsShared: ['baseCurrency', 'fiatValue', 'symbol', ...buitenVelden], }; } const regels = limitedList(rijen, ({ p, m, punt }) => { const v = t.toAi(p, { entity: 'asset' }); const f = isMetal(p) ? metalFactor(p.unit) : 1; const stuk = [`${v.symbol}: ${+(m.units / f).toFixed(8)} ${v.unit}`]; if (m.value != null) stuk.push(`waarde ${money(m.value)} tegen ${money(punt.fiatPrice * f)} per ${v.unit}`); else stuk.push('geen koers, dus geen waarde'); stuk.push(`ingelegd ${money(m.invested)}`); if (m.unrealized != null) stuk.push(`ongerealiseerd ${money(m.unrealized)}`); if (m.avgPrice != null) stuk.push(`gemiddelde aankoopkoers ${money(m.avgPrice * f)}`); return `- ${stuk.join(', ')}`; }).lines; return { text: [kop, '', ...buitenRegel, 'Per asset:', ...regels, '', 'De koers is het laatst opgeslagen koerspunt; dit proces haalt geen verse koersen op.', ].join('\n'), fieldsShared: ['baseCurrency', 'symbol', 'unit', 'decimals', 'amount', 'balance', 'fiatPrice', 'fiatFee', 'fiatValue', ...buitenVelden], }; }, }); export const WEALTH_TOOLS = Object.freeze([wealthSummary]);