Files
AtreidesAssetsFree/tests/wealth/metrics.test.mjs
T
2026-10-29 11:57:29 +01:00

203 lines
8.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Generated, do not edit. sha256:78fe0c105c60d329e375c6de82599c303dbce923361c391f4536e4a44fb6d65d
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { assetMetrics, saleResult, feeResult, combinePositions } from '../../shared/wealth-metrics.js';
const positie = ({ decimals = 8, net, buys = [], fee = 0, gift = 0 }) => ({
decimals,
net,
buy_events: buys.length,
buy_amount: buys.reduce((s, b) => s + b.amount, 0),
buy_spot_raw: buys.reduce((s, b) => s + b.amount * b.price, 0),
buy_fee_total: fee,
gift_amount: gift,
});
describe('assetMetrics: de waarde', () => {
it('rekent de kleinste eenheid om naar hele eenheden en dan naar euro', () => {
const m = assetMetrics(positie({ net: 10e8, buys: [{ amount: 10e8, price: 100 }] }), 110);
assert.equal(m.units, 10);
assert.equal(m.value, 1100);
});
it('geeft géén waarde zonder koers, en geen nul', () => {
const m = assetMetrics(positie({ net: 3e8, buys: [{ amount: 3e8, price: 25 }] }), null);
assert.equal(m.value, null);
assert.equal(m.unrealized, null);
assert.equal(m.invested, 75, 'de inleg komt uit de boekingen en heeft geen koers van nu nodig');
assert.equal(m.avgPrice, 25);
});
it('laat de divisors tegen elkaar wegvallen in de gemiddelde aankoopkoers', () => {
const m = assetMetrics(positie({
net: 2e8, buys: [{ amount: 1e8, price: 100 }, { amount: 1e8, price: 120 }],
}), 110);
assert.equal(m.avgPrice, 110);
assert.equal(m.invested, 220);
});
});
describe('assetMetrics: ongerealiseerd tegenover ingelegd', () => {
it('meet ongerealiseerd tegen de gemiddelde aankoopkoers en niet tegen de inleg', () => {
const m = assetMetrics(positie({ net: 60e8, buys: [{ amount: 100e8, price: 10 }] }), 10);
assert.equal(m.unrealized, 0);
assert.equal(m.invested, 1000);
assert.equal(m.value, 600);
});
it('telt de kosten bij de kostprijs en niet bij de koers', () => {
const m = assetMetrics(positie({ net: 10e8, buys: [{ amount: 10e8, price: 10 }], fee: 5 }), 10);
assert.equal(m.invested, 105);
assert.equal(m.avgPrice, 10);
assert.equal(m.avgCost, 10.5);
assert.equal(m.unrealized, -5);
assert.equal(m.avgPremium, 5, 'de premie is per gebeurtenis');
});
it('rekent de LINK-aankoop van de gebruiker zoals de afschriften', () => {
const m = assetMetrics(positie({ net: 5e8, buys: [{ amount: 5e8, price: 19.968 }], fee: 0.15 }), 12.65);
assert.equal(Math.round(m.unrealized * 100) / 100, -36.74);
assert.equal(m.avgPrice, 19.968, 'de getoonde aankoopkoers blijft zonder kosten');
});
it('verdeelt de kosten naar rato over wat er na een verkoop over is', () => {
const m = assetMetrics(positie({ net: 60e8, buys: [{ amount: 100e8, price: 10 }], fee: 20 }), 10);
assert.equal(Math.round(m.unrealized * 100) / 100, -12);
});
it('geeft geen gemiddelden zonder aankopen', () => {
const m = assetMetrics(positie({ net: 5e8 }), 20);
assert.equal(m.avgPrice, null);
assert.equal(m.avgPremium, null);
assert.equal(m.unrealized, null, 'zonder aankoopkoers is er niets om tegen af te meten');
assert.equal(m.value, 100, 'de waarde bestaat wél: die heeft alleen de stand en de koers nodig');
});
});
describe('saleResult', () => {
it('trekt de kostprijs inclusief kosten af van de netto opbrengst', () => {
const r = saleResult(positie({ net: 10e8, buys: [{ amount: 10e8, price: 10 }], fee: 5 }),
{ amount: 4e8, fiatPrice: 12, fiatFee: 1 });
assert.equal(Math.round(r * 100) / 100, 5);
});
it('sluit aan op ongerealiseerd: samen is het waarde plus opbrengst min betaald', () => {
const voor = positie({ net: 10e8, buys: [{ amount: 10e8, price: 10 }], fee: 5 });
const r = saleResult(voor, { amount: 4e8, fiatPrice: 12, fiatFee: 1 });
const na = assetMetrics({ ...voor, net: 6e8 }, 12);
assert.equal(Math.round((r + na.unrealized) * 100) / 100, 72 + 47 - 105);
});
it('geeft null als het verkochte geen bekende kostprijs heeft', () => {
assert.equal(saleResult(positie({ net: 5e8 }), { amount: 5e8, fiatPrice: 20, fiatFee: 0 }), null);
});
it('telt de fee in eenheden mee als verkocht, niet als opbrengst', () => {
const voor = positie({ net: 10e8, buys: [{ amount: 10e8, price: 10 }] });
const r = saleResult(voor, { amount: 4e8, fiatPrice: 10, fiatFee: 0, unitFee: 1e8 });
assert.equal(r, -10);
const na = assetMetrics({ ...voor, net: 5e8 }, 12);
assert.equal(r + na.unrealized, 60 + 40 - 100);
});
});
describe('feeResult', () => {
it('rekent de fee-eenheden tegen de gemiddelde kostprijs als verlies', () => {
const r = feeResult(positie({ net: 10e8, buys: [{ amount: 10e8, price: 10 }], fee: 5 }), 1e8);
assert.equal(r, -10.5);
});
it('geeft null als de fee-eenheden geen bekende kostprijs hebben', () => {
assert.equal(feeResult(positie({ net: 5e8 }), 1e8), null);
});
it('kost niets als de fee uit een gift komt', () => {
assert.equal(feeResult(positie({ net: 5e8, gift: 5e8 }), 1e8), 0);
});
});
describe('assetMetrics: metaal rekent in grammen en niet in ounces', () => {
it('houdt de eenheid van het asset aan, wat die ook is', () => {
const m = assetMetrics(positie({
decimals: 4, net: 300e4,
buys: [{ amount: 200e4, price: 50 }, { amount: 100e4, price: 50 }],
}), 63);
assert.equal(m.units, 300);
assert.equal(m.value, 18900);
assert.equal(m.invested, 15000);
assert.equal(m.unrealized, 3900);
});
it('telt de premie bij de kostprijs, in grammen', () => {
const OZT = 31.1034768;
const m = assetMetrics(positie({
decimals: 4, net: OZT * 1e4, buys: [{ amount: OZT * 1e4, price: 1000 / OZT }], fee: 25,
}), 1025 / OZT);
assert.equal(Math.round(m.unrealized * 100) / 100, 0);
});
});
describe('assetMetrics: een gift draagt kostprijs nul', () => {
it('meet alleen de betaalde eenheden tegen de gemiddelde aankoopkoers', () => {
const m = assetMetrics(positie({ net: 150e8, buys: [{ amount: 100e8, price: 10 }], gift: 50e8 }), 20);
assert.equal(m.value, 3000);
assert.equal(m.unrealized, 2000);
assert.equal(m.avgPrice, 10, 'de getoonde aankoopkoers blijft van de aankopen');
assert.equal(m.invested, 1000, 'en de inleg ook: aan een gift is niets uitgegeven');
});
it('geeft de hele waarde als er alleen een gift is', () => {
const m = assetMetrics(positie({ net: 50e8, gift: 50e8 }), 20);
assert.equal(m.avgPrice, null, 'er is nog steeds geen aankoopkoers om te tonen');
assert.equal(m.value, 1000);
assert.equal(m.unrealized, 1000);
});
it('houdt een positie zonder gift op een streepje als er niets gekocht is', () => {
const m = assetMetrics(positie({ net: 5e8 }), 20);
assert.equal(m.unrealized, null);
});
it('laat een weggegeven gift tegen de ontvangen gift wegvallen', () => {
const m = assetMetrics(positie({ net: 100e8, buys: [{ amount: 100e8, price: 10 }], gift: 0 }), 20);
assert.equal(m.unrealized, 1000);
});
it('telt bij verkoop eerst de betaalde eenheden, dan de gift tegen nul', () => {
const r = saleResult(positie({ net: 150e8, buys: [{ amount: 100e8, price: 10 }], gift: 50e8 }),
{ amount: 120e8, fiatPrice: 20, fiatFee: 0 });
assert.equal(r, 1400);
});
it('gaat niet onder nul als de positie kleiner is dan de gift', () => {
const m = assetMetrics(positie({ net: 10e8, buys: [{ amount: 100e8, price: 10 }], gift: 50e8 }), 20);
assert.equal(m.unrealized, 200, 'de hele reststand is gift, dus kostprijs nul');
});
});
describe('combinePositions', () => {
it('weegt het gemiddelde naar wat er gekocht is, niet per account', () => {
const groot = positie({ net: 3e8, buys: [{ amount: 3e8, price: 100 }], fee: 6 });
const klein = positie({ net: 1e8, buys: [{ amount: 1e8, price: 200 }], fee: 2 });
const m = assetMetrics(combinePositions([groot, klein]), 150);
assert.equal(m.units, 4);
assert.equal(m.avgPrice, 125, '(3 × 100 + 1 × 200) / 4, en niet (100 + 200) / 2');
assert.equal(m.avgPremium, 4, 'de kosten per aankoop: 8 over twee aankopen');
assert.equal(m.invested, 508);
assert.equal(m.unrealized, 600 - 508);
});
it('telt een gift mee tegen kostprijs nul, zoals bij één account', () => {
const gekocht = positie({ net: 1e8, buys: [{ amount: 1e8, price: 100 }] });
const gekregen = positie({ net: 1e8, gift: 1e8 });
assert.equal(assetMetrics(combinePositions([gekocht, gekregen]), 150).unrealized, 200);
});
it('geeft bij geen enkele positie een lege positie en geen fout', () => {
const m = assetMetrics(combinePositions([]), 150);
assert.equal(m.units, 0);
assert.equal(m.avgPrice, null);
});
});