stock-bot/libs/utils/src/calculations/basic-calculations.ts

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