UNPKG

roi-annualizedroi-irr-calculator

Version:

A lightweight, zero-dependency financial calculator for common investment metrics like ROI, Annualized ROI, and Internal Rate of Return (IRR).

98 lines (82 loc) 3.43 kB
/** * Calculates the Simple Return on Investment (ROI). * * @param {object} args - The arguments for the ROI calculation. * @param {number} args.initialInvestment - The initial amount of the investment. * @param {number} args.finalValue - The final value of the investment. * @returns {number} The ROI as a decimal. */ function roi({ initialInvestment, finalValue }) { if (initialInvestment === 0) { throw new Error('Initial investment cannot be zero.'); } return (finalValue - initialInvestment) / initialInvestment; } /** * Calculates the Annualized Return on Investment. * * @param {object} args - The arguments for the calculation. * @param {number} args.initialInvestment - The initial amount of the investment. * @param {number} args.finalValue - The final value of the investment. * @param {number} args.years - The duration of the investment in years. * @returns {number} The annualized ROI as a decimal. */ function annualizedRoi({ initialInvestment, finalValue, years }) { if (years === 0) { throw new Error('Years cannot be zero for annualized ROI calculation.'); } const simpleRoi = roi({ initialInvestment, finalValue }); return Math.pow(1 + simpleRoi, 1 / years) - 1; } const { differenceInDays } = require('date-fns'); /** * Calculates the Internal Rate of Return (IRR) for a series of transactions. * Transactions should be an array of objects, each with a 'date' and 'amount'. * Deposits are negative, withdrawals/final value are positive. * * @param {Array<object>} transactions - An array of transaction objects. * @param {Date|string} transactions[].date - The date of the transaction. * @param {number} transactions[].amount - The amount of the transaction. * @returns {number} The IRR as a decimal. */ function irr(transactions) { // Basic validation if (!Array.isArray(transactions) || transactions.length < 2) { throw new Error('IRR calculation requires at least two transactions.'); } // Sort transactions by date just in case they are not ordered const sortedTransactions = transactions.sort((a, b) => new Date(a.date) - new Date(b.date)); const startDate = new Date(sortedTransactions[0].date); const cashFlows = sortedTransactions.map(tx => ({ amount: tx.amount, years: differenceInDays(new Date(tx.date), startDate) / 365.25 })); // NPV function const npv = (rate) => cashFlows.reduce((acc, cf) => acc + cf.amount / Math.pow(1 + rate, cf.years), 0); // Derivative of NPV function const npvDerivative = (rate) => cashFlows.reduce((acc, cf) => { if (cf.years === 0) return acc; return acc - (cf.years * cf.amount) / Math.pow(1 + rate, cf.years + 1); }, 0); let guess = 0.1; // Initial guess for IRR const maxIterations = 100; const tolerance = 1e-7; for (let i = 0; i < maxIterations; i++) { const npvValue = npv(guess); const derivativeValue = npvDerivative(guess); if (Math.abs(derivativeValue) < tolerance) { break; // Avoid division by zero } const newGuess = guess - npvValue / derivativeValue; if (Math.abs(newGuess - guess) < tolerance) { return newGuess; // Converged } guess = newGuess; } throw new Error('IRR calculation did not converge. Check your cash flows.'); } module.exports = { roi, annualizedRoi, irr };