// Generated, do not edit. sha256:effc21e27380a5223a30516e074971aec0e56f100cf6c94c8684cfdf36fcca0e import { CONFIG } from '../../shared/config.js'; export function weightOf(atIso, fromIso, untilIso) { const from = Date.parse(fromIso); const until = Date.parse(untilIso); const at = Date.parse(atIso); if (!Number.isFinite(from) || !Number.isFinite(until) || !Number.isFinite(at)) return null; const span = until - from; if (span <= 0) return 1; return Math.min(1, Math.max(0, (until - at) / span)); } export function averageCapital(begin, flows = [], { fromIso, untilIso } = {}) { if (typeof begin !== 'number' || !Number.isFinite(begin)) return null; let som = begin; for (const f of flows) { const w = weightOf(f?.atIso, fromIso, untilIso); const fiat = Number(f?.fiat); if (w == null || !Number.isFinite(fiat)) continue; som += fiat * w; } return som; } export function modifiedDietz({ gain, begin, flows = [], fromIso, untilIso } = {}) { if (typeof gain !== 'number' || !Number.isFinite(gain)) return null; const noemer = averageCapital(begin, flows, { fromIso, untilIso }); if (noemer == null || !(noemer > 0)) return null; return gain / noemer; } export function dailyFlows(cumulative = []) { return cumulative.map((v, i) => (i === 0 ? 0 : (Number(v) || 0) - (Number(cumulative[i - 1]) || 0))); } export function timeWeighted(values = [], flows = [], outflows = []) { let factor = 1; let measured = false; for (const f of dailyFactors(values, flows, outflows)) { if (f == null) continue; factor *= f; measured = true; } return measured ? factor - 1 : null; } export function dailyFactors(values = [], flows = [], outflows = []) { if (!Array.isArray(values)) return []; const out = values.map(() => null); let closed = false; for (let i = 1; i < values.length; i++) { const f = Number(flows[i]) || 0; const inflow = Math.max(f, 0); const outflow = Math.max(-f, 0) + (Number(outflows[i]) || 0); const previous = Number(values[i - 1]); if (closed) { const reopen = inflow > 0 && (!(previous > 0) || inflow >= CONFIG.WEALTH_RETURN.REOPEN_MULTIPLE * previous); if (!reopen) continue; closed = false; } const end = Number(values[i]); const base = previous + inflow; if (!Number.isFinite(end) || !Number.isFinite(base) || !(base > 0)) continue; out[i] = (end + outflow) / base; if (outflow > 0 && end < CONFIG.WEALTH_RETURN.DUST_SHARE * base) closed = true; } return out; } export function lastDayHeld(units = [], from = 0, to = 0) { let i = Math.min(to, units.length - 1); while (i > from && !units[i]) i--; return i >= from && units[i] ? i : null; } export function windowTwr(reeks = {}, venster = {}) { const s = windowSlices(reeks, venster); return s ? timeWeighted(s.values, s.inflows, s.outflows) : null; } export function windowFactors(reeks = {}, venster = {}) { const out = (reeks.waarden || []).map(() => null); const s = windowSlices(reeks, venster); if (!s) return out; dailyFactors(s.values, s.inflows, s.outflows).forEach((f, k) => { const i = s.first + k; if (i >= 0 && i < out.length) out[i] = f; }); return out; } function windowSlices(reeks, venster) { const waarden = reeks.waarden || []; const van = Number(venster.van) || 0; const start = reeks.eersteDag == null ? 0 : Number(reeks.eersteDag) - 1; const vanaf = Math.max(start, van > 0 ? van : -1); if (reeks.eenheden && lastDayHeld(reeks.eenheden, Math.max(vanaf, 0), venster.tot) == null) return null; const tot = Math.min(venster.tot, waarden.length - 1) + 1; const nulDag = vanaf < 0 ? [0] : []; const snee = r => [...nulDag, ...(r || []).slice(Math.max(vanaf, 0), tot)]; const { erin, eruit } = flowsOf(snee(reeks.inleg), snee(reeks.ankers), reeks.uit ? snee(reeks.uit) : []); return { values: snee(waarden), inflows: erin, outflows: eruit, first: vanaf }; } export function flowsOf(inleg = [], ankers = [], uit = []) { const bruto = dailyFlows(inleg.map((v, i) => (Number(v) || 0) + (Number(uit[i]) || 0))); const verkocht = dailyFlows(uit); const anker = dailyFlows(ankers); return { erin: bruto.map((v, i) => v + Math.max(anker[i] || 0, 0)), eruit: bruto.map((_, i) => (verkocht[i] || 0) + Math.max(-(anker[i] || 0), 0)), }; } const IRR_EPS = 1e-7; const IRR_STAPPEN = 200; const IRR_MIN = -0.9999, IRR_MAX = 10; function contanteWaarde(kasstromen, rente) { let som = 0; for (const k of kasstromen) som += k.fiat / Math.pow(1 + rente, k.jaren); return som; } export function irr(flows = [], { fromIso } = {}) { const start = Date.parse(fromIso ?? flows[0]?.atIso); if (!Number.isFinite(start)) return null; const kasstromen = []; for (const f of flows) { const at = Date.parse(f?.atIso); const fiat = Number(f?.fiat); if (!Number.isFinite(at) || !Number.isFinite(fiat)) return null; kasstromen.push({ fiat, jaren: (at - start) / (365.2425 * 24 * 60 * 60 * 1000) }); } if (!kasstromen.some(k => k.fiat > 0) || !kasstromen.some(k => k.fiat < 0)) return null; let laag = IRR_MIN, hoog = IRR_MAX; let fLaag = contanteWaarde(kasstromen, laag); let fHoog = contanteWaarde(kasstromen, hoog); if (!Number.isFinite(fLaag) || !Number.isFinite(fHoog) || fLaag * fHoog > 0) return null; for (let i = 0; i < IRR_STAPPEN; i++) { const midden = (laag + hoog) / 2; const fMidden = contanteWaarde(kasstromen, midden); if (!Number.isFinite(fMidden)) return null; if (Math.abs(fMidden) < IRR_EPS || (hoog - laag) / 2 < IRR_EPS) return midden; if (fLaag * fMidden <= 0) { hoog = midden; fHoog = fMidden; } else { laag = midden; fLaag = fMidden; } } return (laag + hoog) / 2; }