// 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); }); });