running prettier for cleanup
This commit is contained in:
parent
fe7733aeb5
commit
d85cd58acd
151 changed files with 29158 additions and 27966 deletions
|
|
@ -1,391 +1,429 @@
|
|||
/**
|
||||
* 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);
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,166 +1,175 @@
|
|||
/**
|
||||
* Comprehensive Financial Calculations Library
|
||||
*
|
||||
* This module provides a complete set of financial calculations for trading and investment analysis.
|
||||
* Organized into logical categories for easy use and maintenance.
|
||||
*/
|
||||
|
||||
// Core interfaces for financial data
|
||||
export interface OHLCVData {
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface PriceData {
|
||||
price: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
// Financial calculation result interfaces
|
||||
export interface PortfolioMetrics {
|
||||
totalValue: number;
|
||||
totalReturn: number;
|
||||
totalReturnPercent: number;
|
||||
dailyReturn: number;
|
||||
dailyReturnPercent: number;
|
||||
maxDrawdown: number;
|
||||
sharpeRatio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
volatility: number;
|
||||
}
|
||||
|
||||
export interface RiskMetrics {
|
||||
var95: number; // Value at Risk 95%
|
||||
var99: number; // Value at Risk 99%
|
||||
cvar95: number; // Conditional VaR 95%
|
||||
maxDrawdown: number;
|
||||
volatility: number;
|
||||
downside_deviation: number;
|
||||
calmar_ratio: number;
|
||||
sortino_ratio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
sharpeRatio: number;
|
||||
treynorRatio: number;
|
||||
trackingError: number;
|
||||
informationRatio: number;
|
||||
}
|
||||
|
||||
export interface TechnicalIndicators {
|
||||
sma: number[];
|
||||
ema: number[];
|
||||
rsi: number[];
|
||||
macd: { macd: number[], signal: number[], histogram: number[] };
|
||||
bollinger: { upper: number[], middle: number[], lower: number[] };
|
||||
atr: number[];
|
||||
stochastic: { k: number[], d: number[] };
|
||||
williams_r: number[];
|
||||
cci: number[];
|
||||
momentum: number[];
|
||||
roc: number[];
|
||||
}
|
||||
|
||||
// Additional interfaces for new functionality
|
||||
export interface TradeExecution {
|
||||
entry: number;
|
||||
exit: number;
|
||||
peak?: number;
|
||||
trough?: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface MarketData {
|
||||
price: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
bid?: number;
|
||||
ask?: number;
|
||||
bidSize?: number;
|
||||
askSize?: number;
|
||||
}
|
||||
|
||||
export interface BacktestResults {
|
||||
trades: TradeExecution[];
|
||||
equityCurve: Array<{ value: number; date: Date }>;
|
||||
|
||||
performance: PortfolioMetrics;
|
||||
riskMetrics: RiskMetrics;
|
||||
drawdownAnalysis: any; // Import from performance-metrics
|
||||
}
|
||||
|
||||
// Export all calculation functions
|
||||
export * from './basic-calculations';
|
||||
export * from './technical-indicators';
|
||||
export * from './risk-metrics';
|
||||
export * from './portfolio-analytics';
|
||||
export * from './options-pricing';
|
||||
export * from './position-sizing';
|
||||
export * from './performance-metrics';
|
||||
export * from './market-statistics';
|
||||
export * from './volatility-models';
|
||||
export * from './correlation-analysis';
|
||||
|
||||
// Import specific functions for convenience functions
|
||||
import {
|
||||
sma, ema, rsi, macd, bollingerBands, atr, stochastic,
|
||||
williamsR, cci, momentum, roc
|
||||
} from './technical-indicators';
|
||||
import { calculateRiskMetrics } from './risk-metrics';
|
||||
import { calculateStrategyMetrics } from './performance-metrics';
|
||||
|
||||
// Convenience function to calculate all technical indicators at once
|
||||
export function calculateAllTechnicalIndicators(
|
||||
ohlcv: OHLCVData[],
|
||||
periods: { sma?: number; ema?: number; rsi?: number; atr?: number } = {}
|
||||
): TechnicalIndicators {
|
||||
const {
|
||||
sma: smaPeriod = 20,
|
||||
ema: emaPeriod = 20,
|
||||
rsi: rsiPeriod = 14,
|
||||
atr: atrPeriod = 14
|
||||
} = periods;
|
||||
|
||||
const closes = ohlcv.map(d => d.close);
|
||||
|
||||
return {
|
||||
sma: sma(closes, smaPeriod),
|
||||
ema: ema(closes, emaPeriod),
|
||||
rsi: rsi(closes, rsiPeriod),
|
||||
macd: macd(closes),
|
||||
bollinger: bollingerBands(closes),
|
||||
atr: atr(ohlcv, atrPeriod),
|
||||
stochastic: stochastic(ohlcv),
|
||||
williams_r: williamsR(ohlcv),
|
||||
cci: cci(ohlcv),
|
||||
momentum: momentum(closes),
|
||||
roc: roc(closes)
|
||||
};
|
||||
}
|
||||
|
||||
// Convenience function for comprehensive portfolio analysis
|
||||
export function analyzePortfolio(
|
||||
returns: number[],
|
||||
equityCurve: Array<{ value: number; date: Date }>,
|
||||
benchmarkReturns?: number[],
|
||||
riskFreeRate: number = 0.02
|
||||
): {
|
||||
performance: PortfolioMetrics;
|
||||
risk: RiskMetrics;
|
||||
trades?: any;
|
||||
drawdown?: any;
|
||||
} {
|
||||
const performance = calculateStrategyMetrics(equityCurve, benchmarkReturns, riskFreeRate);
|
||||
const equityValues = equityCurve.map(point => point.value);
|
||||
const risk = calculateRiskMetrics(returns, equityValues, benchmarkReturns, riskFreeRate);
|
||||
|
||||
return {
|
||||
performance,
|
||||
risk
|
||||
};
|
||||
}
|
||||
// Import specific functions for convenience functions
|
||||
import { calculateStrategyMetrics } from './performance-metrics';
|
||||
import { calculateRiskMetrics } from './risk-metrics';
|
||||
import {
|
||||
atr,
|
||||
bollingerBands,
|
||||
cci,
|
||||
ema,
|
||||
macd,
|
||||
momentum,
|
||||
roc,
|
||||
rsi,
|
||||
sma,
|
||||
stochastic,
|
||||
williamsR,
|
||||
} from './technical-indicators';
|
||||
|
||||
/**
|
||||
* Comprehensive Financial Calculations Library
|
||||
*
|
||||
* This module provides a complete set of financial calculations for trading and investment analysis.
|
||||
* Organized into logical categories for easy use and maintenance.
|
||||
*/
|
||||
|
||||
// Core interfaces for financial data
|
||||
export interface OHLCVData {
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface PriceData {
|
||||
price: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
// Financial calculation result interfaces
|
||||
export interface PortfolioMetrics {
|
||||
totalValue: number;
|
||||
totalReturn: number;
|
||||
totalReturnPercent: number;
|
||||
dailyReturn: number;
|
||||
dailyReturnPercent: number;
|
||||
maxDrawdown: number;
|
||||
sharpeRatio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
volatility: number;
|
||||
}
|
||||
|
||||
export interface RiskMetrics {
|
||||
var95: number; // Value at Risk 95%
|
||||
var99: number; // Value at Risk 99%
|
||||
cvar95: number; // Conditional VaR 95%
|
||||
maxDrawdown: number;
|
||||
volatility: number;
|
||||
downside_deviation: number;
|
||||
calmar_ratio: number;
|
||||
sortino_ratio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
sharpeRatio: number;
|
||||
treynorRatio: number;
|
||||
trackingError: number;
|
||||
informationRatio: number;
|
||||
}
|
||||
|
||||
export interface TechnicalIndicators {
|
||||
sma: number[];
|
||||
ema: number[];
|
||||
rsi: number[];
|
||||
macd: { macd: number[]; signal: number[]; histogram: number[] };
|
||||
bollinger: { upper: number[]; middle: number[]; lower: number[] };
|
||||
atr: number[];
|
||||
stochastic: { k: number[]; d: number[] };
|
||||
williams_r: number[];
|
||||
cci: number[];
|
||||
momentum: number[];
|
||||
roc: number[];
|
||||
}
|
||||
|
||||
// Additional interfaces for new functionality
|
||||
export interface TradeExecution {
|
||||
entry: number;
|
||||
exit: number;
|
||||
peak?: number;
|
||||
trough?: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface MarketData {
|
||||
price: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
bid?: number;
|
||||
ask?: number;
|
||||
bidSize?: number;
|
||||
askSize?: number;
|
||||
}
|
||||
|
||||
export interface BacktestResults {
|
||||
trades: TradeExecution[];
|
||||
equityCurve: Array<{ value: number; date: Date }>;
|
||||
|
||||
performance: PortfolioMetrics;
|
||||
riskMetrics: RiskMetrics;
|
||||
drawdownAnalysis: any; // Import from performance-metrics
|
||||
}
|
||||
|
||||
// Export all calculation functions
|
||||
export * from './basic-calculations';
|
||||
export * from './technical-indicators';
|
||||
export * from './risk-metrics';
|
||||
export * from './portfolio-analytics';
|
||||
export * from './options-pricing';
|
||||
export * from './position-sizing';
|
||||
export * from './performance-metrics';
|
||||
export * from './market-statistics';
|
||||
export * from './volatility-models';
|
||||
export * from './correlation-analysis';
|
||||
|
||||
// Convenience function to calculate all technical indicators at once
|
||||
export function calculateAllTechnicalIndicators(
|
||||
ohlcv: OHLCVData[],
|
||||
periods: { sma?: number; ema?: number; rsi?: number; atr?: number } = {}
|
||||
): TechnicalIndicators {
|
||||
const {
|
||||
sma: smaPeriod = 20,
|
||||
ema: emaPeriod = 20,
|
||||
rsi: rsiPeriod = 14,
|
||||
atr: atrPeriod = 14,
|
||||
} = periods;
|
||||
|
||||
const closes = ohlcv.map(d => d.close);
|
||||
|
||||
return {
|
||||
sma: sma(closes, smaPeriod),
|
||||
ema: ema(closes, emaPeriod),
|
||||
rsi: rsi(closes, rsiPeriod),
|
||||
macd: macd(closes),
|
||||
bollinger: bollingerBands(closes),
|
||||
atr: atr(ohlcv, atrPeriod),
|
||||
stochastic: stochastic(ohlcv),
|
||||
williams_r: williamsR(ohlcv),
|
||||
cci: cci(ohlcv),
|
||||
momentum: momentum(closes),
|
||||
roc: roc(closes),
|
||||
};
|
||||
}
|
||||
|
||||
// Convenience function for comprehensive portfolio analysis
|
||||
export function analyzePortfolio(
|
||||
returns: number[],
|
||||
equityCurve: Array<{ value: number; date: Date }>,
|
||||
benchmarkReturns?: number[],
|
||||
riskFreeRate: number = 0.02
|
||||
): {
|
||||
performance: PortfolioMetrics;
|
||||
risk: RiskMetrics;
|
||||
trades?: any;
|
||||
drawdown?: any;
|
||||
} {
|
||||
const performance = calculateStrategyMetrics(equityCurve, benchmarkReturns, riskFreeRate);
|
||||
const equityValues = equityCurve.map(point => point.value);
|
||||
const risk = calculateRiskMetrics(returns, equityValues, benchmarkReturns, riskFreeRate);
|
||||
|
||||
return {
|
||||
performance,
|
||||
risk,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,375 +1,387 @@
|
|||
/**
|
||||
* Risk Metrics and Analysis
|
||||
* Comprehensive risk measurement tools for portfolio and trading analysis
|
||||
*/
|
||||
|
||||
import { RiskMetrics, treynorRatio } from './index';
|
||||
|
||||
/**
|
||||
* Calculate Value at Risk (VaR) using historical simulation
|
||||
*/
|
||||
export function valueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const sortedReturns = [...returns].sort((a, b) => a - b);
|
||||
const index = Math.floor((1 - confidenceLevel) * sortedReturns.length);
|
||||
|
||||
return sortedReturns[index] || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Conditional Value at Risk (CVaR/Expected Shortfall)
|
||||
*/
|
||||
export function conditionalValueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const sortedReturns = [...returns].sort((a, b) => a - b);
|
||||
const cutoffIndex = Math.floor((1 - confidenceLevel) * sortedReturns.length);
|
||||
|
||||
if (cutoffIndex === 0) return sortedReturns[0];
|
||||
|
||||
const tailReturns = sortedReturns.slice(0, cutoffIndex);
|
||||
return tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate parametric VaR using normal distribution
|
||||
*/
|
||||
export function parametricVaR(
|
||||
returns: number[],
|
||||
confidenceLevel: number = 0.95,
|
||||
portfolioValue: number = 1
|
||||
): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
// Z-score for confidence level (normal distribution)
|
||||
const zScore = getZScore(confidenceLevel);
|
||||
|
||||
return portfolioValue * (mean - zScore * stdDev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate maximum drawdown
|
||||
*/
|
||||
export function maxDrawdown(equityCurve: number[]): number {
|
||||
if (equityCurve.length < 2) return 0;
|
||||
|
||||
let maxDD = 0;
|
||||
let peak = equityCurve[0];
|
||||
|
||||
for (let i = 1; i < equityCurve.length; i++) {
|
||||
if (equityCurve[i] > peak) {
|
||||
peak = equityCurve[i];
|
||||
} else {
|
||||
const drawdown = (peak - equityCurve[i]) / peak;
|
||||
maxDD = Math.max(maxDD, drawdown);
|
||||
}
|
||||
}
|
||||
|
||||
return maxDD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate downside deviation
|
||||
*/
|
||||
export function downsideDeviation(returns: number[], targetReturn: number = 0): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const downsideReturns = returns.filter(ret => ret < targetReturn);
|
||||
|
||||
if (downsideReturns.length === 0) return 0;
|
||||
|
||||
const sumSquaredDownside = downsideReturns.reduce(
|
||||
(sum, ret) => sum + Math.pow(ret - targetReturn, 2),
|
||||
0
|
||||
);
|
||||
|
||||
return Math.sqrt(sumSquaredDownside / returns.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Sharpe ratio
|
||||
*/
|
||||
export function sharpeRatio(returns: number[], riskFreeRate: number = 0): number {
|
||||
if (returns.length < 2) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
if (stdDev === 0) return 0;
|
||||
|
||||
return (mean - riskFreeRate) / stdDev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate beta coefficient
|
||||
*/
|
||||
export function beta(portfolioReturns: number[], marketReturns: number[]): number {
|
||||
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const n = portfolioReturns.length;
|
||||
const portfolioMean = portfolioReturns.reduce((sum, ret) => sum + ret, 0) / n;
|
||||
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / n;
|
||||
|
||||
let covariance = 0;
|
||||
let marketVariance = 0;
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const portfolioDiff = portfolioReturns[i] - portfolioMean;
|
||||
const marketDiff = marketReturns[i] - marketMean;
|
||||
|
||||
covariance += portfolioDiff * marketDiff;
|
||||
marketVariance += marketDiff * marketDiff;
|
||||
}
|
||||
|
||||
return marketVariance === 0 ? 0 : covariance / marketVariance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate alpha
|
||||
*/
|
||||
export function alpha(
|
||||
portfolioReturns: number[],
|
||||
marketReturns: number[],
|
||||
riskFreeRate: number = 0
|
||||
): number {
|
||||
const portfolioMean = portfolioReturns.reduce((sum, ret) => sum + ret, 0) / portfolioReturns.length;
|
||||
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / marketReturns.length;
|
||||
const portfolioBeta = beta(portfolioReturns, marketReturns);
|
||||
|
||||
return portfolioMean - (riskFreeRate + portfolioBeta * (marketMean - riskFreeRate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate tracking error
|
||||
*/
|
||||
export function trackingError(portfolioReturns: number[], benchmarkReturns: number[]): number {
|
||||
if (portfolioReturns.length !== benchmarkReturns.length || portfolioReturns.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const activeReturns = portfolioReturns.map((ret, i) => ret - benchmarkReturns[i]);
|
||||
const mean = activeReturns.reduce((sum, ret) => sum + ret, 0) / activeReturns.length;
|
||||
|
||||
const variance = activeReturns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (activeReturns.length - 1);
|
||||
|
||||
return Math.sqrt(variance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate volatility (standard deviation of returns)
|
||||
*/
|
||||
export function volatility(returns: number[]): number {
|
||||
if (returns.length < 2) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
|
||||
|
||||
return Math.sqrt(variance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate annualized volatility
|
||||
*/
|
||||
export function annualizedVolatility(returns: number[], periodsPerYear: number = 252): number {
|
||||
return volatility(returns) * Math.sqrt(periodsPerYear);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate skewness (measure of asymmetry)
|
||||
*/
|
||||
export function skewness(returns: number[]): number {
|
||||
if (returns.length < 3) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
if (stdDev === 0) return 0;
|
||||
|
||||
const skew = returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 3), 0) / returns.length;
|
||||
|
||||
return skew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate kurtosis (measure of tail heaviness)
|
||||
*/
|
||||
export function kurtosis(returns: number[]): number {
|
||||
if (returns.length < 4) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
if (stdDev === 0) return 0;
|
||||
|
||||
const kurt = returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 4), 0) / returns.length;
|
||||
|
||||
return kurt - 3; // Excess kurtosis (subtract 3 for normal distribution baseline)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate comprehensive risk metrics
|
||||
*/
|
||||
export function calculateRiskMetrics(
|
||||
returns: number[],
|
||||
equityCurve: number[],
|
||||
marketReturns?: number[],
|
||||
riskFreeRate: number = 0
|
||||
): RiskMetrics {
|
||||
if (returns.length === 0) {
|
||||
return {
|
||||
var95: 0,
|
||||
var99: 0,
|
||||
cvar95: 0,
|
||||
maxDrawdown: 0,
|
||||
volatility: 0,
|
||||
downside_deviation: 0,
|
||||
calmar_ratio: 0,
|
||||
sortino_ratio: 0,
|
||||
beta: 0,
|
||||
alpha: 0,
|
||||
sharpeRatio: 0,
|
||||
treynorRatio: 0,
|
||||
trackingError: 0,
|
||||
informationRatio: 0
|
||||
};
|
||||
}
|
||||
|
||||
const portfolioVolatility = volatility(returns);
|
||||
const portfolioMean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
// Calculate VaR
|
||||
const var95Value = valueAtRisk(returns, 0.95);
|
||||
const var99Value = valueAtRisk(returns, 0.99);
|
||||
const cvar95Value = conditionalValueAtRisk(returns, 0.95);
|
||||
|
||||
// Calculate max drawdown
|
||||
const maxDD = maxDrawdown(equityCurve);
|
||||
|
||||
// Calculate downside deviation
|
||||
const downsideDeviationValue = downsideDeviation(returns);
|
||||
// Calculate ratios
|
||||
const calmarRatio = maxDD > 0 ? portfolioMean / maxDD : 0;
|
||||
const sortinoRatio = downsideDeviationValue > 0 ? (portfolioMean - riskFreeRate) / downsideDeviationValue : 0;
|
||||
const sharpeRatio = portfolioVolatility > 0 ? (portfolioMean - riskFreeRate) / portfolioVolatility : 0;
|
||||
|
||||
let portfolioBeta = 0;
|
||||
let portfolioAlpha = 0;
|
||||
let portfolioTreynorRatio = 0;
|
||||
let portfolioTrackingError = 0;
|
||||
let informationRatio = 0;
|
||||
|
||||
if (marketReturns && marketReturns.length === returns.length) {
|
||||
portfolioBeta = beta(returns, marketReturns);
|
||||
portfolioAlpha = alpha(returns, marketReturns, riskFreeRate);
|
||||
portfolioTreynorRatio = treynorRatio(returns, marketReturns, riskFreeRate);
|
||||
portfolioTrackingError = trackingError(returns, marketReturns);
|
||||
informationRatio = portfolioTrackingError > 0 ? portfolioAlpha / portfolioTrackingError : 0;
|
||||
}
|
||||
return {
|
||||
var95: var95Value,
|
||||
var99: var99Value,
|
||||
cvar95: cvar95Value,
|
||||
maxDrawdown: maxDD,
|
||||
volatility: portfolioVolatility,
|
||||
downside_deviation: downsideDeviationValue,
|
||||
calmar_ratio: calmarRatio,
|
||||
sortino_ratio: sortinoRatio,
|
||||
beta: portfolioBeta,
|
||||
alpha: portfolioAlpha,
|
||||
sharpeRatio,
|
||||
treynorRatio: portfolioTreynorRatio,
|
||||
trackingError: portfolioTrackingError,
|
||||
informationRatio
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get Z-score for confidence level
|
||||
* This implementation handles arbitrary confidence levels
|
||||
*/
|
||||
function getZScore(confidenceLevel: number): number {
|
||||
// First check our lookup table for common values (more precise)
|
||||
const zScores: { [key: string]: number } = {
|
||||
'0.90': 1.282,
|
||||
'0.95': 1.645,
|
||||
'0.975': 1.960,
|
||||
'0.99': 2.326,
|
||||
'0.995': 2.576
|
||||
};
|
||||
|
||||
const key = confidenceLevel.toString();
|
||||
if (zScores[key]) return zScores[key];
|
||||
|
||||
// For arbitrary confidence levels, use approximation
|
||||
if (confidenceLevel < 0.5) return -getZScore(1 - confidenceLevel);
|
||||
|
||||
if (confidenceLevel >= 0.999) return 3.09; // Cap at 99.9% for numerical stability
|
||||
|
||||
// Approximation of inverse normal CDF
|
||||
const y = Math.sqrt(-2.0 * Math.log(1.0 - confidenceLevel));
|
||||
return y - (2.515517 + 0.802853 * y + 0.010328 * y * y) /
|
||||
(1.0 + 1.432788 * y + 0.189269 * y * y + 0.001308 * y * y * y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate portfolio risk contribution
|
||||
*/
|
||||
export function riskContribution(
|
||||
weights: number[],
|
||||
covarianceMatrix: number[][],
|
||||
portfolioVolatility: number
|
||||
): number[] {
|
||||
const n = weights.length;
|
||||
const contributions: number[] = [];
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
let marginalContribution = 0;
|
||||
|
||||
for (let j = 0; j < n; j++) {
|
||||
marginalContribution += weights[j] * covarianceMatrix[i][j];
|
||||
}
|
||||
|
||||
const contribution = (weights[i] * marginalContribution) / Math.pow(portfolioVolatility, 2);
|
||||
contributions.push(contribution);
|
||||
}
|
||||
|
||||
return contributions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Ulcer Index
|
||||
*/
|
||||
export function ulcerIndex(equityCurve: Array<{ value: number; date: Date }>): number {
|
||||
let sumSquaredDrawdown = 0;
|
||||
let peak = equityCurve[0].value;
|
||||
|
||||
for (const point of equityCurve) {
|
||||
peak = Math.max(peak, point.value);
|
||||
const drawdownPercent = (peak - point.value) / peak * 100;
|
||||
sumSquaredDrawdown += drawdownPercent * drawdownPercent;
|
||||
}
|
||||
|
||||
return Math.sqrt(sumSquaredDrawdown / equityCurve.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate risk-adjusted return (RAR)
|
||||
*/
|
||||
export function riskAdjustedReturn(
|
||||
portfolioReturn: number,
|
||||
portfolioRisk: number,
|
||||
riskFreeRate: number = 0
|
||||
): number {
|
||||
if (portfolioRisk === 0) return 0;
|
||||
return (portfolioReturn - riskFreeRate) / portfolioRisk;
|
||||
}
|
||||
/**
|
||||
* Risk Metrics and Analysis
|
||||
* Comprehensive risk measurement tools for portfolio and trading analysis
|
||||
*/
|
||||
|
||||
import { RiskMetrics, treynorRatio } from './index';
|
||||
|
||||
/**
|
||||
* Calculate Value at Risk (VaR) using historical simulation
|
||||
*/
|
||||
export function valueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const sortedReturns = [...returns].sort((a, b) => a - b);
|
||||
const index = Math.floor((1 - confidenceLevel) * sortedReturns.length);
|
||||
|
||||
return sortedReturns[index] || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Conditional Value at Risk (CVaR/Expected Shortfall)
|
||||
*/
|
||||
export function conditionalValueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const sortedReturns = [...returns].sort((a, b) => a - b);
|
||||
const cutoffIndex = Math.floor((1 - confidenceLevel) * sortedReturns.length);
|
||||
|
||||
if (cutoffIndex === 0) return sortedReturns[0];
|
||||
|
||||
const tailReturns = sortedReturns.slice(0, cutoffIndex);
|
||||
return tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate parametric VaR using normal distribution
|
||||
*/
|
||||
export function parametricVaR(
|
||||
returns: number[],
|
||||
confidenceLevel: number = 0.95,
|
||||
portfolioValue: number = 1
|
||||
): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance =
|
||||
returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
// Z-score for confidence level (normal distribution)
|
||||
const zScore = getZScore(confidenceLevel);
|
||||
|
||||
return portfolioValue * (mean - zScore * stdDev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate maximum drawdown
|
||||
*/
|
||||
export function maxDrawdown(equityCurve: number[]): number {
|
||||
if (equityCurve.length < 2) return 0;
|
||||
|
||||
let maxDD = 0;
|
||||
let peak = equityCurve[0];
|
||||
|
||||
for (let i = 1; i < equityCurve.length; i++) {
|
||||
if (equityCurve[i] > peak) {
|
||||
peak = equityCurve[i];
|
||||
} else {
|
||||
const drawdown = (peak - equityCurve[i]) / peak;
|
||||
maxDD = Math.max(maxDD, drawdown);
|
||||
}
|
||||
}
|
||||
|
||||
return maxDD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate downside deviation
|
||||
*/
|
||||
export function downsideDeviation(returns: number[], targetReturn: number = 0): number {
|
||||
if (returns.length === 0) return 0;
|
||||
|
||||
const downsideReturns = returns.filter(ret => ret < targetReturn);
|
||||
|
||||
if (downsideReturns.length === 0) return 0;
|
||||
|
||||
const sumSquaredDownside = downsideReturns.reduce(
|
||||
(sum, ret) => sum + Math.pow(ret - targetReturn, 2),
|
||||
0
|
||||
);
|
||||
|
||||
return Math.sqrt(sumSquaredDownside / returns.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Sharpe ratio
|
||||
*/
|
||||
export function sharpeRatio(returns: number[], riskFreeRate: number = 0): number {
|
||||
if (returns.length < 2) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance =
|
||||
returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
if (stdDev === 0) return 0;
|
||||
|
||||
return (mean - riskFreeRate) / stdDev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate beta coefficient
|
||||
*/
|
||||
export function beta(portfolioReturns: number[], marketReturns: number[]): number {
|
||||
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const n = portfolioReturns.length;
|
||||
const portfolioMean = portfolioReturns.reduce((sum, ret) => sum + ret, 0) / n;
|
||||
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / n;
|
||||
|
||||
let covariance = 0;
|
||||
let marketVariance = 0;
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const portfolioDiff = portfolioReturns[i] - portfolioMean;
|
||||
const marketDiff = marketReturns[i] - marketMean;
|
||||
|
||||
covariance += portfolioDiff * marketDiff;
|
||||
marketVariance += marketDiff * marketDiff;
|
||||
}
|
||||
|
||||
return marketVariance === 0 ? 0 : covariance / marketVariance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate alpha
|
||||
*/
|
||||
export function alpha(
|
||||
portfolioReturns: number[],
|
||||
marketReturns: number[],
|
||||
riskFreeRate: number = 0
|
||||
): number {
|
||||
const portfolioMean =
|
||||
portfolioReturns.reduce((sum, ret) => sum + ret, 0) / portfolioReturns.length;
|
||||
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / marketReturns.length;
|
||||
const portfolioBeta = beta(portfolioReturns, marketReturns);
|
||||
|
||||
return portfolioMean - (riskFreeRate + portfolioBeta * (marketMean - riskFreeRate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate tracking error
|
||||
*/
|
||||
export function trackingError(portfolioReturns: number[], benchmarkReturns: number[]): number {
|
||||
if (portfolioReturns.length !== benchmarkReturns.length || portfolioReturns.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const activeReturns = portfolioReturns.map((ret, i) => ret - benchmarkReturns[i]);
|
||||
const mean = activeReturns.reduce((sum, ret) => sum + ret, 0) / activeReturns.length;
|
||||
|
||||
const variance =
|
||||
activeReturns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) /
|
||||
(activeReturns.length - 1);
|
||||
|
||||
return Math.sqrt(variance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate volatility (standard deviation of returns)
|
||||
*/
|
||||
export function volatility(returns: number[]): number {
|
||||
if (returns.length < 2) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance =
|
||||
returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
|
||||
|
||||
return Math.sqrt(variance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate annualized volatility
|
||||
*/
|
||||
export function annualizedVolatility(returns: number[], periodsPerYear: number = 252): number {
|
||||
return volatility(returns) * Math.sqrt(periodsPerYear);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate skewness (measure of asymmetry)
|
||||
*/
|
||||
export function skewness(returns: number[]): number {
|
||||
if (returns.length < 3) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
if (stdDev === 0) return 0;
|
||||
|
||||
const skew =
|
||||
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 3), 0) / returns.length;
|
||||
|
||||
return skew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate kurtosis (measure of tail heaviness)
|
||||
*/
|
||||
export function kurtosis(returns: number[]): number {
|
||||
if (returns.length < 4) return 0;
|
||||
|
||||
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
if (stdDev === 0) return 0;
|
||||
|
||||
const kurt =
|
||||
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 4), 0) / returns.length;
|
||||
|
||||
return kurt - 3; // Excess kurtosis (subtract 3 for normal distribution baseline)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate comprehensive risk metrics
|
||||
*/
|
||||
export function calculateRiskMetrics(
|
||||
returns: number[],
|
||||
equityCurve: number[],
|
||||
marketReturns?: number[],
|
||||
riskFreeRate: number = 0
|
||||
): RiskMetrics {
|
||||
if (returns.length === 0) {
|
||||
return {
|
||||
var95: 0,
|
||||
var99: 0,
|
||||
cvar95: 0,
|
||||
maxDrawdown: 0,
|
||||
volatility: 0,
|
||||
downside_deviation: 0,
|
||||
calmar_ratio: 0,
|
||||
sortino_ratio: 0,
|
||||
beta: 0,
|
||||
alpha: 0,
|
||||
sharpeRatio: 0,
|
||||
treynorRatio: 0,
|
||||
trackingError: 0,
|
||||
informationRatio: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const portfolioVolatility = volatility(returns);
|
||||
const portfolioMean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
|
||||
// Calculate VaR
|
||||
const var95Value = valueAtRisk(returns, 0.95);
|
||||
const var99Value = valueAtRisk(returns, 0.99);
|
||||
const cvar95Value = conditionalValueAtRisk(returns, 0.95);
|
||||
|
||||
// Calculate max drawdown
|
||||
const maxDD = maxDrawdown(equityCurve);
|
||||
|
||||
// Calculate downside deviation
|
||||
const downsideDeviationValue = downsideDeviation(returns);
|
||||
// Calculate ratios
|
||||
const calmarRatio = maxDD > 0 ? portfolioMean / maxDD : 0;
|
||||
const sortinoRatio =
|
||||
downsideDeviationValue > 0 ? (portfolioMean - riskFreeRate) / downsideDeviationValue : 0;
|
||||
const sharpeRatio =
|
||||
portfolioVolatility > 0 ? (portfolioMean - riskFreeRate) / portfolioVolatility : 0;
|
||||
|
||||
let portfolioBeta = 0;
|
||||
let portfolioAlpha = 0;
|
||||
let portfolioTreynorRatio = 0;
|
||||
let portfolioTrackingError = 0;
|
||||
let informationRatio = 0;
|
||||
|
||||
if (marketReturns && marketReturns.length === returns.length) {
|
||||
portfolioBeta = beta(returns, marketReturns);
|
||||
portfolioAlpha = alpha(returns, marketReturns, riskFreeRate);
|
||||
portfolioTreynorRatio = treynorRatio(returns, marketReturns, riskFreeRate);
|
||||
portfolioTrackingError = trackingError(returns, marketReturns);
|
||||
informationRatio = portfolioTrackingError > 0 ? portfolioAlpha / portfolioTrackingError : 0;
|
||||
}
|
||||
return {
|
||||
var95: var95Value,
|
||||
var99: var99Value,
|
||||
cvar95: cvar95Value,
|
||||
maxDrawdown: maxDD,
|
||||
volatility: portfolioVolatility,
|
||||
downside_deviation: downsideDeviationValue,
|
||||
calmar_ratio: calmarRatio,
|
||||
sortino_ratio: sortinoRatio,
|
||||
beta: portfolioBeta,
|
||||
alpha: portfolioAlpha,
|
||||
sharpeRatio,
|
||||
treynorRatio: portfolioTreynorRatio,
|
||||
trackingError: portfolioTrackingError,
|
||||
informationRatio,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get Z-score for confidence level
|
||||
* This implementation handles arbitrary confidence levels
|
||||
*/
|
||||
function getZScore(confidenceLevel: number): number {
|
||||
// First check our lookup table for common values (more precise)
|
||||
const zScores: { [key: string]: number } = {
|
||||
'0.90': 1.282,
|
||||
'0.95': 1.645,
|
||||
'0.975': 1.96,
|
||||
'0.99': 2.326,
|
||||
'0.995': 2.576,
|
||||
};
|
||||
|
||||
const key = confidenceLevel.toString();
|
||||
if (zScores[key]) return zScores[key];
|
||||
|
||||
// For arbitrary confidence levels, use approximation
|
||||
if (confidenceLevel < 0.5) return -getZScore(1 - confidenceLevel);
|
||||
|
||||
if (confidenceLevel >= 0.999) return 3.09; // Cap at 99.9% for numerical stability
|
||||
|
||||
// Approximation of inverse normal CDF
|
||||
const y = Math.sqrt(-2.0 * Math.log(1.0 - confidenceLevel));
|
||||
return (
|
||||
y -
|
||||
(2.515517 + 0.802853 * y + 0.010328 * y * y) /
|
||||
(1.0 + 1.432788 * y + 0.189269 * y * y + 0.001308 * y * y * y)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate portfolio risk contribution
|
||||
*/
|
||||
export function riskContribution(
|
||||
weights: number[],
|
||||
covarianceMatrix: number[][],
|
||||
portfolioVolatility: number
|
||||
): number[] {
|
||||
const n = weights.length;
|
||||
const contributions: number[] = [];
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
let marginalContribution = 0;
|
||||
|
||||
for (let j = 0; j < n; j++) {
|
||||
marginalContribution += weights[j] * covarianceMatrix[i][j];
|
||||
}
|
||||
|
||||
const contribution = (weights[i] * marginalContribution) / Math.pow(portfolioVolatility, 2);
|
||||
contributions.push(contribution);
|
||||
}
|
||||
|
||||
return contributions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Ulcer Index
|
||||
*/
|
||||
export function ulcerIndex(equityCurve: Array<{ value: number; date: Date }>): number {
|
||||
let sumSquaredDrawdown = 0;
|
||||
let peak = equityCurve[0].value;
|
||||
|
||||
for (const point of equityCurve) {
|
||||
peak = Math.max(peak, point.value);
|
||||
const drawdownPercent = ((peak - point.value) / peak) * 100;
|
||||
sumSquaredDrawdown += drawdownPercent * drawdownPercent;
|
||||
}
|
||||
|
||||
return Math.sqrt(sumSquaredDrawdown / equityCurve.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate risk-adjusted return (RAR)
|
||||
*/
|
||||
export function riskAdjustedReturn(
|
||||
portfolioReturn: number,
|
||||
portfolioRisk: number,
|
||||
riskFreeRate: number = 0
|
||||
): number {
|
||||
if (portfolioRisk === 0) return 0;
|
||||
return (portfolioReturn - riskFreeRate) / portfolioRisk;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,55 +1,55 @@
|
|||
/**
|
||||
* Date and time utilities for working with market data
|
||||
*/
|
||||
export const dateUtils = {
|
||||
/**
|
||||
* Check if a date is a trading day (Monday-Friday, non-holiday)
|
||||
* This is a simplified implementation - a real version would check market holidays
|
||||
*/
|
||||
isTradingDay(date: Date): boolean {
|
||||
const day = date.getDay();
|
||||
return day > 0 && day < 6; // Mon-Fri
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the next trading day from a given date
|
||||
*/
|
||||
getNextTradingDay(date: Date): Date {
|
||||
const nextDay = new Date(date);
|
||||
nextDay.setDate(nextDay.getDate() + 1);
|
||||
|
||||
while (!this.isTradingDay(nextDay)) {
|
||||
nextDay.setDate(nextDay.getDate() + 1);
|
||||
}
|
||||
|
||||
return nextDay;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the previous trading day from a given date
|
||||
*/
|
||||
getPreviousTradingDay(date: Date): Date {
|
||||
const prevDay = new Date(date);
|
||||
prevDay.setDate(prevDay.getDate() - 1);
|
||||
|
||||
while (!this.isTradingDay(prevDay)) {
|
||||
prevDay.setDate(prevDay.getDate() - 1);
|
||||
}
|
||||
|
||||
return prevDay;
|
||||
},
|
||||
|
||||
/**
|
||||
* Format a date as YYYY-MM-DD
|
||||
*/
|
||||
formatDate(date: Date): string {
|
||||
return date.toISOString().split('T')[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a date string in YYYY-MM-DD format
|
||||
*/
|
||||
parseDate(dateStr: string): Date {
|
||||
return new Date(dateStr);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Date and time utilities for working with market data
|
||||
*/
|
||||
export const dateUtils = {
|
||||
/**
|
||||
* Check if a date is a trading day (Monday-Friday, non-holiday)
|
||||
* This is a simplified implementation - a real version would check market holidays
|
||||
*/
|
||||
isTradingDay(date: Date): boolean {
|
||||
const day = date.getDay();
|
||||
return day > 0 && day < 6; // Mon-Fri
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the next trading day from a given date
|
||||
*/
|
||||
getNextTradingDay(date: Date): Date {
|
||||
const nextDay = new Date(date);
|
||||
nextDay.setDate(nextDay.getDate() + 1);
|
||||
|
||||
while (!this.isTradingDay(nextDay)) {
|
||||
nextDay.setDate(nextDay.getDate() + 1);
|
||||
}
|
||||
|
||||
return nextDay;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the previous trading day from a given date
|
||||
*/
|
||||
getPreviousTradingDay(date: Date): Date {
|
||||
const prevDay = new Date(date);
|
||||
prevDay.setDate(prevDay.getDate() - 1);
|
||||
|
||||
while (!this.isTradingDay(prevDay)) {
|
||||
prevDay.setDate(prevDay.getDate() - 1);
|
||||
}
|
||||
|
||||
return prevDay;
|
||||
},
|
||||
|
||||
/**
|
||||
* Format a date as YYYY-MM-DD
|
||||
*/
|
||||
formatDate(date: Date): string {
|
||||
return date.toISOString().split('T')[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a date string in YYYY-MM-DD format
|
||||
*/
|
||||
parseDate(dateStr: string): Date {
|
||||
return new Date(dateStr);
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
export * from './dateUtils';
|
||||
export * from './calculations/index';
|
||||
export * from './dateUtils';
|
||||
export * from './calculations/index';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue