stock-bot/libs/utils/src/calculations/basic-calculations.ts
2025-06-13 13:38:02 -04:00

447 lines
10 KiB
TypeScript

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