Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,150 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in new issue
Block a user