/** * Basic Financial Calculations * Core mathematical functions for financial analysis */ /** * Calculate percentage change between two values */ export function percentageChange(oldValue: number, newValue: number): number { if (oldValue === 0) return 0; return ((newValue - oldValue) / oldValue) * 100; } /** * Calculate simple return */ export function simpleReturn(initialPrice: number, finalPrice: number): number { if (initialPrice === 0) return 0; return (finalPrice - initialPrice) / initialPrice; } /** * Calculate logarithmic return */ export function logReturn(initialPrice: number, finalPrice: number): number { if (initialPrice <= 0 || finalPrice <= 0) return 0; return Math.log(finalPrice / initialPrice); } /** * Calculate compound annual growth rate (CAGR) */ export function cagr(startValue: number, endValue: number, years: number): number { if (years <= 0 || startValue <= 0 || endValue <= 0) return 0; return Math.pow(endValue / startValue, 1 / years) - 1; } /** * Calculate annualized return from periodic returns */ export function annualizeReturn(periodicReturn: number, periodsPerYear: number): number { return Math.pow(1 + periodicReturn, periodsPerYear) - 1; } /** * Calculate annualized volatility from periodic returns */ export function annualizeVolatility(periodicVolatility: number, periodsPerYear: number): number { return periodicVolatility * Math.sqrt(periodsPerYear); } /** * Calculate present value */ export function presentValue(futureValue: number, rate: number, periods: number): number { return futureValue / Math.pow(1 + rate, periods); } /** * Calculate future value */ export function futureValue(presentValue: number, rate: number, periods: number): number { return presentValue * Math.pow(1 + rate, periods); } /** * Calculate net present value of cash flows */ export function netPresentValue(cashFlows: number[], discountRate: number): number { return cashFlows.reduce((npv, cashFlow, index) => { return npv + cashFlow / Math.pow(1 + discountRate, index); }, 0); } /** * Calculate internal rate of return (IRR) using Newton-Raphson method */ export function internalRateOfReturn(cashFlows: number[], guess: number = 0.1, maxIterations: number = 100): number { let rate = guess; for (let i = 0; i < maxIterations; i++) { let npv = 0; let dnpv = 0; for (let j = 0; j < cashFlows.length; j++) { npv += cashFlows[j] / Math.pow(1 + rate, j); dnpv += -j * cashFlows[j] / Math.pow(1 + rate, j + 1); } if (Math.abs(npv) < 1e-10) break; if (Math.abs(dnpv) < 1e-10) break; rate = rate - npv / dnpv; } return rate; } /** * Calculate payback period */ export function paybackPeriod(initialInvestment: number, cashFlows: number[]): number { let cumulativeCashFlow = 0; for (let i = 0; i < cashFlows.length; i++) { cumulativeCashFlow += cashFlows[i]; if (cumulativeCashFlow >= initialInvestment) { return i + 1 - (cumulativeCashFlow - initialInvestment) / cashFlows[i]; } } return -1; // Never pays back } /** * Calculate compound interest */ export function compoundInterest( principal: number, rate: number, periods: number, compoundingFrequency: number = 1 ): number { return principal * Math.pow(1 + rate / compoundingFrequency, compoundingFrequency * periods); } /** * Calculate effective annual rate */ export function effectiveAnnualRate(nominalRate: number, compoundingFrequency: number): number { return Math.pow(1 + nominalRate / compoundingFrequency, compoundingFrequency) - 1; } /** * Calculate bond price given yield */ export function bondPrice( faceValue: number, couponRate: number, yieldToMaturity: number, periodsToMaturity: number, paymentsPerYear: number = 2 ): number { const couponPayment = (faceValue * couponRate) / paymentsPerYear; const discountRate = yieldToMaturity / paymentsPerYear; let price = 0; // Present value of coupon payments for (let i = 1; i <= periodsToMaturity; i++) { price += couponPayment / Math.pow(1 + discountRate, i); } // Present value of face value price += faceValue / Math.pow(1 + discountRate, periodsToMaturity); return price; } /** * Calculate bond yield given price (Newton-Raphson approximation) */ export function bondYield( price: number, faceValue: number, couponRate: number, periodsToMaturity: number, paymentsPerYear: number = 2, guess: number = 0.05 ): number { let yield_ = guess; const maxIterations = 100; const tolerance = 1e-8; for (let i = 0; i < maxIterations; i++) { const calculatedPrice = bondPrice(faceValue, couponRate, yield_, periodsToMaturity, paymentsPerYear); const diff = calculatedPrice - price; if (Math.abs(diff) < tolerance) break; // Numerical derivative const delta = 0.0001; const priceUp = bondPrice(faceValue, couponRate, yield_ + delta, periodsToMaturity, paymentsPerYear); const derivative = (priceUp - calculatedPrice) / delta; if (Math.abs(derivative) < tolerance) break; yield_ = yield_ - diff / derivative; } return yield_; } /** * Calculate duration (Macaulay duration) */ export function macaulayDuration( faceValue: number, couponRate: number, yieldToMaturity: number, periodsToMaturity: number, paymentsPerYear: number = 2 ): number { const couponPayment = (faceValue * couponRate) / paymentsPerYear; const discountRate = yieldToMaturity / paymentsPerYear; const bondPriceValue = bondPrice(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear); let weightedTime = 0; // Weighted time of coupon payments for (let i = 1; i <= periodsToMaturity; i++) { const presentValue = couponPayment / Math.pow(1 + discountRate, i); weightedTime += (i * presentValue) / bondPriceValue; } // Weighted time of face value const faceValuePV = faceValue / Math.pow(1 + discountRate, periodsToMaturity); weightedTime += (periodsToMaturity * faceValuePV) / bondPriceValue; return weightedTime / paymentsPerYear; // Convert to years } /** * Calculate modified duration */ export function modifiedDuration( faceValue: number, couponRate: number, yieldToMaturity: number, periodsToMaturity: number, paymentsPerYear: number = 2 ): number { const macDuration = macaulayDuration(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear); return macDuration / (1 + yieldToMaturity / paymentsPerYear); } /** * Calculate bond convexity */ export function bondConvexity( faceValue: number, couponRate: number, yieldToMaturity: number, periodsToMaturity: number, paymentsPerYear: number = 2 ): number { const couponPayment = (faceValue * couponRate) / paymentsPerYear; const discountRate = yieldToMaturity / paymentsPerYear; let convexity = 0; const bondPriceValue = bondPrice(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear); for (let i = 1; i <= periodsToMaturity; i++) { const presentValue = couponPayment / Math.pow(1 + discountRate, i); convexity += (i * (i + 1) * presentValue) / Math.pow(1 + discountRate, 2); } const faceValuePV = faceValue / Math.pow(1 + discountRate, periodsToMaturity); convexity += (periodsToMaturity * (periodsToMaturity + 1) * faceValuePV) / Math.pow(1 + discountRate, 2); return convexity / (bondPriceValue * paymentsPerYear * paymentsPerYear); } /** * Calculate dollar duration */ export function dollarDuration( faceValue: number, couponRate: number, yieldToMaturity: number, periodsToMaturity: number, paymentsPerYear: number = 2, basisPointChange: number = 0.01 // 1 basis point = 0.01% ): number { const modifiedDur = modifiedDuration(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear); const bondPriceValue = bondPrice(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear); return modifiedDur * bondPriceValue * basisPointChange; } /** * Calculate accrued interest */ export function accruedInterest( faceValue: number, couponRate: number, daysSinceLastCoupon: number, daysInCouponPeriod: number ): number { return (faceValue * couponRate) * (daysSinceLastCoupon / daysInCouponPeriod); } /** * Calculate clean price */ export function cleanPrice(dirtyPrice: number, accruedInterestValue: number): number { return dirtyPrice - accruedInterestValue; } /** * Calculate dirty price */ export function dirtyPrice(cleanPriceValue: number, accruedInterestValue: number): number { return cleanPriceValue + accruedInterestValue; } /** * Calculate dividend discount model (DDM) */ export function dividendDiscountModel( currentDividend: number, growthRate: number, discountRate: number ): number { if (discountRate <= growthRate) return NaN; // Indeterminate return currentDividend * (1 + growthRate) / (discountRate - growthRate); } /** * Calculate weighted average cost of capital (WACC) */ export function weightedAverageCostOfCapital( costOfEquity: number, costOfDebt: number, equityWeight: number, debtWeight: number, taxRate: number ): number { return (equityWeight * costOfEquity) + (debtWeight * costOfDebt * (1 - taxRate)); } /** * Calculate capital asset pricing model (CAPM) */ export function capitalAssetPricingModel( riskFreeRate: number, beta: number, marketRiskPremium: number ): number { return riskFreeRate + beta * marketRiskPremium; } /** * Calculate Treynor ratio */ export function treynorRatio( portfolioReturn: number, riskFreeRate: number, beta: number ): number { return (portfolioReturn - riskFreeRate) / beta; } /** * Calculate hurdle rate */ export function hurdleRate( costOfCapital: number, riskPremium: number ): number { return costOfCapital + riskPremium; } /** * Calculate degree of operating leverage (DOL) */ export function degreeOfOperatingLeverage( contributionMargin: number, operatingIncome: number ): number { return contributionMargin / operatingIncome; } /** * Calculate degree of financial leverage (DFL) */ export function degreeOfFinancialLeverage( ebit: number, earningsBeforeTax: number ): number { return ebit / earningsBeforeTax; } /** * Calculate degree of total leverage (DTL) */ export function degreeOfTotalLeverage( dol: number, dfl: number ): number { return dol * dfl; } /** * Calculate economic value added (EVA) */ export function economicValueAdded( netOperatingProfitAfterTax: number, capitalInvested: number, wacc: number ): number { return netOperatingProfitAfterTax - (capitalInvested * wacc); }